diff --git a/.changeset/30512-platform-objects-ar.md b/.changeset/30512-platform-objects-ar.md new file mode 100644 index 00000000000..c6711a474c2 --- /dev/null +++ b/.changeset/30512-platform-objects-ar.md @@ -0,0 +1,10 @@ +--- +'@objectstack/platform-objects': patch +--- + +i18n(platform-objects): the platform objects, Setup, Studio and Account ship in Arabic + +Clause-②: no +Metadata-protocol: none + +`@objectstack/platform-objects` now carries `ar` beside `en`, `zh-CN`, `ja-JP`, `es-ES` and `zh-Hant`: every `sys_*` object (labels, fields, help, options, views, actions and their dialogs), the metadata-type configuration forms Studio opens, the Setup, Studio and Account navigation, the System Overview dashboard and its datasets, the platform pages' titles, and the inline copy on the user and organization record pages. An administrator or developer reading the platform in Arabic no longer sees it in English. The new bundles pass through the same source-hash fallback as the existing ones, and every hand-authored leaf records the digest of the source it was translated from. The extract config documents `ar` in its `--locales`, so `check:i18n` now gates the new bundles. Package 11 of #30512, second of seven locales; `fr`, `pt-BR`, `ru`, `de` and `ko` follow. diff --git a/packages/platform-objects/scripts/i18n-extract.config.ts b/packages/platform-objects/scripts/i18n-extract.config.ts index 0e44393d7f3..60276d909cb 100644 --- a/packages/platform-objects/scripts/i18n-extract.config.ts +++ b/packages/platform-objects/scripts/i18n-extract.config.ts @@ -8,7 +8,7 @@ * pointed at this file via `bundle-require`: * * os i18n extract packages/platform-objects/scripts/i18n-extract.config.ts \ - * --locales=zh-CN,ja-JP,es-ES,zh-Hant \ + * --locales=zh-CN,ja-JP,es-ES,zh-Hant,ar \ * --fill=default \ * --source-hashes \ * --out=packages/platform-objects/src/apps/translations @@ -222,11 +222,13 @@ import { zhCNObjects } from '../src/apps/translations/zh-CN.objects.generated.js import { jaJPObjects } from '../src/apps/translations/ja-JP.objects.generated.js'; import { esESObjects } from '../src/apps/translations/es-ES.objects.generated.js'; import { zhHantObjects } from '../src/apps/translations/zh-Hant.objects.generated.js'; +import { arObjects } from '../src/apps/translations/ar.objects.generated.js'; import { enMetadataForms } from '../src/apps/translations/en.metadata-forms.generated.js'; import { zhCNMetadataForms } from '../src/apps/translations/zh-CN.metadata-forms.generated.js'; import { jaJPMetadataForms } from '../src/apps/translations/ja-JP.metadata-forms.generated.js'; import { esESMetadataForms } from '../src/apps/translations/es-ES.metadata-forms.generated.js'; import { zhHantMetadataForms } from '../src/apps/translations/zh-Hant.metadata-forms.generated.js'; +import { arMetadataForms } from '../src/apps/translations/ar.metadata-forms.generated.js'; // ── Existing hand-authored app/dashboard/page translations ──────────────── // The per-locale assemblers, NOT the `SetupAppTranslations` wrapper. Each one @@ -250,6 +252,7 @@ import { zhCN } from '../src/apps/translations/zh-CN.js'; import { jaJP } from '../src/apps/translations/ja-JP.js'; import { esES } from '../src/apps/translations/es-ES.js'; import { zhHant } from '../src/apps/translations/zh-Hant.js'; +import { ar } from '../src/apps/translations/ar.js'; /** * The annotation is load-bearing, not decoration (#10868). `defineStack` @@ -351,6 +354,7 @@ const config: ObjectStackDefinition = defineStack({ { 'ja-JP': { ...jaJP, objects: jaJPObjects, metadataForms: jaJPMetadataForms } }, { 'es-ES': { ...esES, objects: esESObjects, metadataForms: esESMetadataForms } }, { 'zh-Hant': { ...zhHant, objects: zhHantObjects, metadataForms: zhHantMetadataForms } }, + { 'ar': { ...ar, objects: arObjects, metadataForms: arMetadataForms } }, ], }); diff --git a/packages/platform-objects/src/apps/dashboards/system-overview-null-label.test.ts b/packages/platform-objects/src/apps/dashboards/system-overview-null-label.test.ts index dc109ed1342..afe7dcffab3 100644 --- a/packages/platform-objects/src/apps/dashboards/system-overview-null-label.test.ts +++ b/packages/platform-objects/src/apps/dashboards/system-overview-null-label.test.ts @@ -31,6 +31,7 @@ describe('System Overview: the null-user bucket reads "System"', () => { 'ja-JP': 'システム', 'es-ES': 'Sistema', 'zh-Hant': '系統', + ar: 'النظام', }); }); diff --git a/packages/platform-objects/src/apps/dashboards/system.datasets.ts b/packages/platform-objects/src/apps/dashboards/system.datasets.ts index 5bb905fa4c8..4096eb43121 100644 --- a/packages/platform-objects/src/apps/dashboards/system.datasets.ts +++ b/packages/platform-objects/src/apps/dashboards/system.datasets.ts @@ -57,7 +57,7 @@ export const SysAuditLogDataset = defineDataset({ label: 'User', field: 'user_id', type: 'lookup', - nullLabel: { en: 'System', 'zh-CN': '系统', 'ja-JP': 'システム', 'es-ES': 'Sistema', 'zh-Hant': '系統' }, + nullLabel: { en: 'System', 'zh-CN': '系统', 'ja-JP': 'システム', 'es-ES': 'Sistema', 'zh-Hant': '系統', ar: 'النظام' }, }, ], measures: [{ name: 'event_count', label: 'Events', aggregate: 'count' }], diff --git a/packages/platform-objects/src/apps/setup-copy-vocabulary.test.ts b/packages/platform-objects/src/apps/setup-copy-vocabulary.test.ts index 5de2d5dd6c2..069ebefcd76 100644 --- a/packages/platform-objects/src/apps/setup-copy-vocabulary.test.ts +++ b/packages/platform-objects/src/apps/setup-copy-vocabulary.test.ts @@ -46,16 +46,19 @@ import { zhCN } from './translations/zh-CN.js'; import { jaJP } from './translations/ja-JP.js'; import { esES } from './translations/es-ES.js'; import { zhHant } from './translations/zh-Hant.js'; +import { ar } from './translations/ar.js'; import { enObjects } from './translations/en.objects.generated.js'; import { zhCNObjects } from './translations/zh-CN.objects.generated.js'; import { jaJPObjects } from './translations/ja-JP.objects.generated.js'; import { esESObjects } from './translations/es-ES.objects.generated.js'; import { zhHantObjects } from './translations/zh-Hant.objects.generated.js'; +import { arObjects } from './translations/ar.objects.generated.js'; import { enMetadataForms } from './translations/en.metadata-forms.generated.js'; import { zhCNMetadataForms } from './translations/zh-CN.metadata-forms.generated.js'; import { jaJPMetadataForms } from './translations/ja-JP.metadata-forms.generated.js'; import { esESMetadataForms } from './translations/es-ES.metadata-forms.generated.js'; import { zhHantMetadataForms } from './translations/zh-Hant.metadata-forms.generated.js'; +import { arMetadataForms } from './translations/ar.metadata-forms.generated.js'; /** The design-record vocabulary an admin-facing string must not carry. */ const DESIGN_RECORD_VOCABULARY: ReadonlyArray = [ @@ -130,13 +133,14 @@ function objectSchemas(...modules: Array>): Map { diff --git a/packages/platform-objects/src/apps/setup-nav-distinct-labels.test.ts b/packages/platform-objects/src/apps/setup-nav-distinct-labels.test.ts index 2619708b056..fce376c27e8 100644 --- a/packages/platform-objects/src/apps/setup-nav-distinct-labels.test.ts +++ b/packages/platform-objects/src/apps/setup-nav-distinct-labels.test.ts @@ -16,8 +16,9 @@ import { zhCN } from './translations/zh-CN.js'; import { jaJP } from './translations/ja-JP.js'; import { esES } from './translations/es-ES.js'; import { zhHant } from './translations/zh-Hant.js'; +import { ar } from './translations/ar.js'; -const LOCALES = { en, 'zh-CN': zhCN, 'ja-JP': jaJP, 'es-ES': esES, 'zh-Hant': zhHant } as const; +const LOCALES = { en, 'zh-CN': zhCN, 'ja-JP': jaJP, 'es-ES': esES, 'zh-Hant': zhHant, ar } as const; type NavItem = { id?: string; label?: string }; diff --git a/packages/platform-objects/src/apps/setup-nav-per-user-stores.test.ts b/packages/platform-objects/src/apps/setup-nav-per-user-stores.test.ts index 6bf8c5668dd..ced8657c2da 100644 --- a/packages/platform-objects/src/apps/setup-nav-per-user-stores.test.ts +++ b/packages/platform-objects/src/apps/setup-nav-per-user-stores.test.ts @@ -111,9 +111,10 @@ describe('(2) the User page lists each per-user store, keyed to its record (#238 'ja-JP': '設定', 'es-ES': 'Preferencias', 'zh-Hant': '偏好設定', + ar: 'التفضيلات', }); expect((tabOf('sys_account')?.label as Rec)?.en).toBe('Linked Accounts'); - expect(Object.keys((tabOf('sys_account')?.label as Rec) ?? {}).sort()).toEqual(['en', 'es-ES', 'ja-JP', 'zh-CN', 'zh-Hant']); + expect(Object.keys((tabOf('sys_account')?.label as Rec) ?? {}).sort()).toEqual(['ar', 'en', 'es-ES', 'ja-JP', 'zh-CN', 'zh-Hant']); }); }); diff --git a/packages/platform-objects/src/apps/translations/app-nav-translation-parity.test.ts b/packages/platform-objects/src/apps/translations/app-nav-translation-parity.test.ts index 4571d39e309..a0f17a40753 100644 --- a/packages/platform-objects/src/apps/translations/app-nav-translation-parity.test.ts +++ b/packages/platform-objects/src/apps/translations/app-nav-translation-parity.test.ts @@ -59,8 +59,9 @@ import { zhCN } from './zh-CN.js'; import { jaJP } from './ja-JP.js'; import { esES } from './es-ES.js'; import { zhHant } from './zh-Hant.js'; +import { ar } from './ar.js'; -const LOCALES = { en, 'zh-CN': zhCN, 'ja-JP': jaJP, 'es-ES': esES, 'zh-Hant': zhHant } as const; +const LOCALES = { en, 'zh-CN': zhCN, 'ja-JP': jaJP, 'es-ES': esES, 'zh-Hant': zhHant, ar } as const; /** Every statically declared nav item of an app, depth-first, with its label. */ function navItems(app: { navigation?: unknown[] }): Array<{ id: string; label?: string }> { diff --git a/packages/platform-objects/src/apps/translations/ar.metadata-forms.generated.ts b/packages/platform-objects/src/apps/translations/ar.metadata-forms.generated.ts new file mode 100644 index 00000000000..e0c3ccfed75 --- /dev/null +++ b/packages/platform-objects/src/apps/translations/ar.metadata-forms.generated.ts @@ -0,0 +1,2815 @@ +// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. + +/** + * Auto-generated by 'os i18n extract' for locale 'ar'. + * Edit translations in place; re-run extract (with --merge) to fill new gaps. + * Do not hand-edit the structure — only the leaf string values. + * Merge only fills gaps: correcting a source label/description does not + * push the correction into a leaf here that already holds a translation — + * a present-but-stale string is not a gap, so it is left as-is. Re-translate + * it by hand when its source changes; nothing here or in `os i18n check` + * tells a leaf a translator updated on purpose from one nobody has looked + * at since the source moved. + */ + +import type { TranslationData } from '@objectstack/spec/system'; + +export const arMetadataForms: NonNullable = { + object: { + label: "الكائن", + sections: { + basics: { + label: "الأساسيات", + description: "الهوية والتسميات والتصنيف." + }, + fields: { + label: "الحقول", + description: "عرِّف نموذج البيانات — يصبح كل إدخال عمودًا في جدول قاعدة البيانات." + }, + capabilities: { + label: "القدرات", + description: "ميزات النظام والإتاحة عبر API." + }, + advanced: { + label: "متقدم", + description: "آلات الحالة والإجراءات والتخزين." + } + }, + fields: { + name: { + label: "الاسم", + helpText: "معرّف فريد بصيغة snake_case (غير قابل للتغيير بعد الإنشاء)" + }, + label: { + label: "التسمية", + helpText: "اسم العرض بصيغة المفرد (مثل \"Account\")" + }, + pluralLabel: { + label: "تسمية الجمع", + helpText: "اسم العرض بصيغة الجمع (مثل \"Accounts\")" + }, + icon: { + label: "الأيقونة", + helpText: "اسم أيقونة Lucide (مثل \"building\"، \"users\")" + }, + description: { + label: "الوصف", + helpText: "توثيق للمطوّرين" + }, + nameField: { + label: "حقل الاسم", + helpText: "الحقل الذي تكون قيمته عنوانًا لكل سجل (مثل \"name\"، \"subject\"). المؤشر المعتمد وفق ADR-0079 — يقرؤه عرض السجل وبحث ObjectQL ومعاينات السجلات المرتبطة." + }, + imageField: { + label: "حقل الصورة", + helpText: "الحقل الذي تكون قيمته صورة كل سجل، وتُرسم بجانب العنوان في ترويسة صفحة السجل. يجب أن يسمّي حقلًا من هذا الكائن نوعه image أو avatar؛ ويُرفض أي اسم آخر عند حفظ الكائن. فارغ: لا صورة للسجل، ولا تُرسم صورة بديلة." + }, + isSystem: { + label: "كائن نظام", + helpText: "كائن نظام (محمي من الحذف؛ تكون مشاركته عامة افتراضيًا)" + }, + highlightFields: { + label: "الحقول المميَّزة", + helpText: "أسماء حقول هذا الكائن، الأهم أولًا — يفوز الإدخال الأول حيث لا يتسع إلا لواحد (ADR-0085). تحدد أعمدة القائمة الافتراضية والبطاقات ومعاينات السجلات الفرعية وشريط الإبراز في صفحة التفاصيل. يُرفض عند النشر أي اسم ليس حقلًا من هذا الكائن." + }, + relatedLists: { + label: "القوائم المرتبطة", + helpText: "ترتيب العمل للقوائم المرتبطة بهذا الكائن في صفحة سجله، الأهم أولًا. كل إدخال اسم كائن فرعي (مثل task)، أو object.field عندما يكون لذلك الكائن الفرعي أكثر من علاقة بهذا الكائن. تأتي القوائم المُدرجة أولًا؛ ويتبعها الباقي بالترتيب المشتق. الاسم الذي لا يطابق أي علاقة تشير إلى هذا الكائن لا يضع شيئًا، لذا يرفضه حفظ الكائن أو التحقق منه." + }, + searchableFields: { + label: "الحقول القابلة للبحث", + helpText: "أسماء الحقول التي يطابقها استعلام $search ‏(ADR-0061): القيمة الافتراضية لمنتقي السجلات والبحث السريع في القوائم والبحث العام؛ ويمكن لطريقة العرض تضييقها. عند عدم تعيينها، يستخدم البحث حقل الاسم/العنوان إضافة إلى الحقول النصية القصيرة. يجب أن يسمّي كل إدخال حقلًا مخزَّنًا من هذا الكائن — ويُرفض عند النشر الاسم المجهول أو حقل الصيغة الافتراضي." + }, + fieldGroups: { + label: "مجموعات الحقول", + helpText: "أقسام مرتبة تجمع حقول هذا الكائن في نموذج الإدخال وصفحة تفاصيل السجل (ADR-0085)؛ ترتيب المصفوفة هو ترتيب العرض. ينضم الحقل إلى مجموعة بذكر مفتاحها في إعداد المجموعة الخاص بالحقل. تأتي الحقول التي لا تنتمي إلى أي مجموعة بعد المجموعات، ولا تُرسم المجموعة التي لا ينضم إليها أي حقل." + }, + "fieldGroups.key": { + label: "المفتاح", + helpText: "مفتاح برمجي بصيغة snake_case، فريد داخل هذا الكائن — ويرفض المخطط أي شيء غير ذلك. تنضم الحقول إلى المجموعة بذكر هذا المفتاح، لذا فإن إعادة تسميته تتركها بلا مجموعة." + }, + "fieldGroups.label": { + label: "التسمية", + helpText: "نص ترويسة قسم المجموعة." + }, + "fieldGroups.icon": { + label: "الأيقونة", + helpText: "اسم أيقونة Lucide يظهر بجانب الترويسة في صفحة تفاصيل السجل (مثل \"banknote\"). لا يعرضها نموذج الإدخال." + }, + "fieldGroups.description": { + label: "الوصف", + helpText: "نص يظهر تحت الترويسة، في نموذج الإدخال وصفحة تفاصيل السجل." + }, + "fieldGroups.relatedList": { + label: "القائمة المرتبطة", + helpText: "يجعل هذا الإدخال جدول قائمة مرتبطة بدلًا من قسم حقول: سمِّ الكائن الفرعي (أو object.field) الذي تُرسم قائمته في هذا الموضع من عمود تفاصيل السجل. لا تحتوي مثل هذه المجموعة على حقول؛ ويُرفض الحقل الذي يذكرها كمجموعة له، وكذلك الاسم الذي لا يطابق أي علاقة تشير إلى هذا الكائن. لا ترسم صفحة السجل هذا الموضع بعد." + }, + "fieldGroups.collapse": { + label: "الطي", + helpText: "ما إذا كان يمكن طي القسم، في نموذج الإدخال وصفحة تفاصيل السجل. عند عدم التعيين: لا." + }, + "fieldGroups.visibleWhen": { + label: "مرئي عندما", + helpText: "شرط CEL على السجل (مثل record.type == 'invoice') — يعرض نموذج الإدخال المجموعة بأكملها، بما فيها الترويسة، فقط ما دام صحيحًا (TRUE)." + }, + "fieldGroups.requiresFeature": { + label: "يتطلب الميزة", + helpText: "ميزة النشر التي تحتاجها المجموعة (مثل multiOrgEnabled): حيث تكون معطّلة، يُخفي نموذج الإدخال وصفحة تفاصيل السجل المجموعة بأكملها. عند اقترانها بـ «مرئي عندما»، لا تظهر المجموعة إلا ما دام الشرطان متحققين. تبقى الحقول قابلة للقراءة عبر API." + }, + fields: { + label: "الحقول", + helpText: "أضف الأعمدة التي سيخزّنها هذا الكائن" + }, + "fields.label": { + label: "التسمية", + helpText: "تسمية العرض" + }, + "fields.type": { + label: "النوع", + helpText: "نوع الحقل" + }, + "fields.description": { + label: "الوصف", + helpText: "توثيق للمطوّرين لهذا العمود" + }, + "fields.required": { + label: "مطلوب", + helpText: "يجب تعيينه في كل سجل" + }, + "fields.unique": { + label: "فريد", + helpText: "منع القيم المكررة" + }, + "fields.readonly": { + label: "للقراءة فقط", + helpText: "مرئي لكن لا يستطيع المستخدم تعديله أبدًا" + }, + "fields.hidden": { + label: "مخفي", + helpText: "مخفي من الواجهة الافتراضية" + }, + "fields.maskingRule": { + label: "قاعدة الإخفاء", + helpText: "إخفاء جزئي: إعداد مسبق ('phone'، 'id_card'، 'bank_account'، 'email'، 'name') أو {\"keepHead\": n, \"keepTail\": m}. تُخفى القيمة ما لم يحمل المستدعي requiredPermissions الخاصة بهذا الحقل" + }, + "fields.searchable": { + label: "قابل للبحث", + helpText: "تضمينه في البحث النصي الكامل" + }, + "fields.sortable": { + label: "قابل للفرز", + helpText: "السماح بالفرز على هذا العمود" + }, + "fields.defaultValue": { + label: "القيمة الافتراضية", + helpText: "القيمة الافتراضية للسجلات الجديدة (قيمة JSON حرفية)" + }, + "fields.placeholder": { + label: "النص النائب", + helpText: "نص تلميح يظهر داخل حقل الإدخال الفارغ؛ ويختفي بمجرد إدخال قيمة" + }, + "fields.maxLength": { + label: "الحد الأقصى للطول", + helpText: "الحد الأقصى لعدد الأحرف" + }, + "fields.minLength": { + label: "الحد الأدنى للطول", + helpText: "الحد الأدنى لعدد الأحرف" + }, + "fields.valueDomain": { + label: "نطاق القيم", + helpText: "المعيار الذي يجب أن تنتمي إليه القيمة المكتوبة؛ وتُرفض الكتابة التي تحمل قيمة من خارجه" + }, + "fields.rows": { + label: "الصفوف", + helpText: "ارتفاع المحرر المضمَّن (صفوف نصية)" + }, + "fields.min": { + label: "الحد الأدنى", + helpText: "أدنى قيمة" + }, + "fields.max": { + label: "الحد الأقصى", + helpText: "أقصى قيمة" + }, + "fields.precision": { + label: "الدقة", + helpText: "إجمالي الأرقام" + }, + "fields.scale": { + label: "المقياس", + helpText: "عدد المنازل العشرية" + }, + "fields.options": { + label: "الخيارات", + helpText: "الخيارات المتاحة" + }, + "fields.options.label": { + label: "التسمية" + }, + "fields.options.value": { + label: "القيمة" + }, + "fields.options.color": { + label: "اللون" + }, + "fields.options.description": { + label: "الوصف" + }, + "fields.options.kind": { + label: "النوع", + helpText: "فئة دورة الحياة (للاختيار ذي القيمة الواحدة / أزرار الاختيار فقط): خطوة على الطريق، أو النهاية الناجحة، أو النهاية غير الناجحة. مطلوبة في كل خيار من خيارات حقل المرحلة." + }, + "fields.reference": { + label: "المرجع", + helpText: "اسم الكائن الهدف. في حقل الشجرة يكون اختياريًا، وإذا أُعطي فيجب أن يكون هذا الكائن (فالشجرة تسلسل هرمي داخل كائنها نفسه — اربط كائنًا مختلفًا بحقل بحث)" + }, + "fields.lookupFilters": { + label: "عوامل تصفية البحث", + helpText: "قواعد التصفية المطبَّقة على المنتقي ({field, operator, value})" + }, + "fields.deleteBehavior": { + label: "سلوك الحذف", + helpText: "ما يحدث عند حذف السجل المُشار إليه" + }, + "fields.multiple": { + label: "متعدد", + helpText: "السماح باختيار عدة سجلات" + }, + "fields.expression": { + label: "التعبير", + helpText: "تعبير CEL الذي تُحسب منه قيمة الحقل. في حقل الصيغة: يُحسب عند القراءة، بلا عمود مخزَّن. في أي حقل آخر: يحسبه المحرك من الصف الذي تخزّنه كل عملية كتابة، ويُخزَّن، فلا تثبت أبدًا قيمة يرسلها العميل." + }, + "fields.returnType": { + label: "نوع الإرجاع", + helpText: "نوع النتيجة للصيغ" + }, + "fields.summaryOperations": { + label: "عمليات التلخيص", + helpText: "التجميع التراكمي: أي كائن فرعي، وأي حقل، وأي تجميع" + }, + "fields.summaryOperations.object": { + label: "الكائن", + helpText: "اسم الكائن الفرعي المصدر" + }, + "fields.summaryOperations.field": { + label: "الحقل", + helpText: "الحقل في الكائن الفرعي المراد تجميعه (يُتجاهل في count)" + }, + "fields.summaryOperations.function": { + label: "الدالة", + helpText: "دالة التجميع" + }, + "fields.autonumberFormat": { + label: "تنسيق الترقيم التلقائي", + helpText: "مثل \"INV-{0000}\"؛ تُدعم رموز التاريخ {YYYY}/{MM}/{DD} والاستبدال بـ {field_name}" + }, + "fields.language": { + label: "اللغة", + helpText: "لغة المحرر (مثل sql، javascript)" + }, + "fields.trackHistory": { + label: "تتبّع السجل", + helpText: "تلخيص هذا الحقل في الخط الزمني لنشاط السجل" + }, + "fields.visibleWhen": { + label: "مرئي عندما", + helpText: "شرط CEL — لا يُعرض الحقل إلا عندما يكون صحيحًا (TRUE)" + }, + "fields.readonlyWhen": { + label: "للقراءة فقط عندما", + helpText: "شرط CEL — يكون الحقل للقراءة فقط عندما يكون صحيحًا (TRUE) (يُفرض من جهة الخادم)" + }, + "fields.requiredWhen": { + label: "مطلوب عندما", + helpText: "شرط CEL — يكون الحقل مطلوبًا عندما يكون صحيحًا (TRUE) (يُفرض من جهة الخادم)" + }, + enable: { + label: "التفعيل", + helpText: "تفعيل/تعطيل ميزات النظام" + }, + "enable.trackHistory": { + label: "تتبّع السجل" + }, + "enable.searchable": { + label: "قابل للبحث" + }, + "enable.apiEnabled": { + label: "الإتاحة عبر API" + }, + "enable.files": { + label: "الملفات" + }, + "enable.feeds": { + label: "الموجزات" + }, + "enable.activities": { + label: "الأنشطة" + }, + "enable.clone": { + label: "الاستنساخ" + }, + "enable.approvalsVisibleToReaders": { + label: "إظهار الموافقات للقرّاء", + helpText: "يرى قرّاء السجل طلبات الموافقة عليه وسجلها، للقراءة فقط، دون أي إجراء موافقة" + }, + validations: { + label: "قواعد التحقق", + helpText: "قواعد التحقق على مستوى الكائن — مصفوفة من كائنات القواعد، مثل [{ \"type\": \"script\", \"name\": \"amount_positive\", \"condition\": \"amount > 0\", \"message\": \"Amount must be positive\" }]. وتُعلَن هنا أيضًا جداول انتقالات آلة الحالة (ADR-0020)" + }, + activityMilestones: { + label: "معالم النشاط", + helpText: "إدخالات في الخط الزمني يُطلقها وصول حقل إلى قيمة (ADR-0052 §5b.2): عندما ينقل تحديثٌ الحقلَ المراقَب إلى القيمة، يكتب مكوّن التدقيق الإضافي ملخّص المَعلم في الخط الزمني لنشاط السجل بدلًا من إدخال تغيير الحقل. يفوز أول مَعلم يطابق." + }, + "activityMilestones.field": { + label: "الحقل", + helpText: "اسم الحقل المراد مراقبته في هذا الكائن (مثل status). لا شيء يتحقق منه عند الحفظ أو النشر: الاسم الذي ليس حقلًا من هذا الكائن لا يُطلق أبدًا." + }, + "activityMilestones.value": { + label: "القيمة", + helpText: "القيمة المخزَّنة التي يجب أن يتغير الحقل إليها، وتُقارن حرفيًا كنص — في حقل الاختيار تكون قيمة الخيار لا تسميته (مثل done). المَعلم على حقل رقمي أو منطقي لا يُطلق أبدًا." + }, + "activityMilestones.summary": { + label: "الملخّص", + helpText: "نص الخط الزمني (مثل \"تم كسب الصفقة: {name}\"). يأخذ الرمز {field_name} قيمة السجل بعد التحديث، ويعرض رمز حقل البحث أو الرئيسي-التفصيلي أو المستخدم عنوان السجل المُشار إليه؛ والرمز الذي لا يسمّي أي حقل يُعرض فارغًا." + }, + "activityMilestones.type": { + label: "النوع", + helpText: "نوع نشاط إدخال الخط الزمني: نوع مدمج مثل completed، أو كلمة من عندك، تُخزَّن كما كُتبت. عند عدم التعيين: updated." + }, + datasource: { + label: "مصدر البيانات", + helpText: "معرّف مصدر البيانات الهدف (الافتراضي: \"default\")" + }, + indexes: { + label: "الفهارس", + helpText: "فهارس قاعدة البيانات على جدول هذا الكائن. ينشئ مشغّل SQL كل فهرس ينقص الجدول عند مزامنته؛ ولا تحذف المزامنة أي فهرس أبدًا." + }, + "indexes.name": { + label: "الاسم", + helpText: "اسم الفهرس الفعلي. عند عدم التعيين: يُولَّد من الجدول والأعمدة (مثل idx_task_status)." + }, + "indexes.fields": { + label: "الحقول", + helpText: "أسماء أعمدة هذا الكائن، بترتيب المفتاح (مثل status، owner). لا يتحقق منها الحفظ؛ ويرفض النشرُ والأمرُ os validate أي اسم ليس حقلًا من هذا الكائن. الحقل الذي ليس عمودًا مخزَّنًا (حقل صيغة مثلًا) يجعل مشغّل SQL يتخطى الفهرس بأكمله، مع خطأ في سجل الخادم." + }, + "indexes.unique": { + label: "فريد", + helpText: "نطاق التفرّد (ADR-0120). عند عدم التعيين: غير فريد. لا تُعرض القيمة المجرّدة المُهمَلة true (وتعني عامًا)؛ والفهرس الذي يحملها يحتفظ بها إلى أن تختار نطاقًا." + }, + ownership: { + label: "الملكية", + helpText: "نموذج ملكية السجلات. عند الغياب يُحَلّ إلى user." + }, + sharingModel: { + label: "نموذج المشاركة", + helpText: "رؤية السجلات الافتراضية على مستوى المؤسسة للمستخدمين الداخليين. الكائن المخصّص الذي يُغفله يُحَلّ إلى private وقت التشغيل (ADR-0090 D1)." + }, + sharing: { + label: "المشاركة", + helpText: "خيارات مشاركة لكل كائن تُحسّن الإعداد الافتراضي على مستوى المؤسسة أعلاه؛ ولا تحلّ محله أبدًا. عند عدم التعيين: يُطبَّق الإعداد الافتراضي لكل خيار." + }, + "sharing.controlledByParent": { + label: "محكوم بالأصل", + helpText: "يُقرأ فقط ما دام sharingModel هو 'controlled by parent'؛ ويُرفض مع أي نموذج مشاركة آخر." + }, + "sharing.controlledByParent.ownerMayEdit": { + label: "يجوز للمالك التعديل", + helpText: "عند التفعيل: يجوز لمالك صف التفاصيل (owner_id الخاص به) تحديث ذلك الصف بمعرّفه مع صلاحية القراءة على السجل الرئيسي؛ أما الآخرون، وكل إدراج وحذف وتحديث متعدد الصفوف وكتابة نيابةً عن الغير، فلا يزال يحتاج إلى صلاحية التعديل على السجل الرئيسي. عند الإيقاف (الافتراضي): تحتاج كل كتابة على صف تفاصيل إلى صلاحية التعديل على سجله الرئيسي." + }, + access: { + label: "الوصول", + helpText: "وضع المنح الشاملة (ADR-0066 D2). عند الغياب يُحَلّ إلى public. يحدد ما إذا كان منح الكائنات '*' في مجموعة أذونات يشمل هذا الكائن؛ أما رؤية السجلات بين المستخدمين فهي sharingModel." + }, + "access.default": { + label: "الافتراضي", + helpText: "public: تشمله منح '*' الشاملة. private: يحتاج إلى منح صريح لكل كائن، ويُستثنى من أمان مستوى الصف الشامل." + }, + requiredPermissions: { + label: "الأذونات المطلوبة", + helpText: "القدرات (systemPermissions في مجموعة الأذونات) التي يجب أن يحملها المستدعي للوصول إلى هذا الكائن، وتُفحص إضافة إلى منح CRUD ‏(ADR-0066 D3). القائمة تقيّد كل العمليات؛ والخريطة {read, create, update, delete} لا تقيّد إلا العمليات التي تذكرها. عند الغياب أو الفراغ: لا قيد على القدرات." + }, + publicSharing: { + label: "المشاركة العامة", + helpText: "سياسة روابط المشاركة: ما إذا كان يمكن نشر سجلات هذا الكائن عبر رابط يفتحه أي شخص يحمله، وبأي شروط. منفصلة عن sharingModel، الذي يشارك مع مستخدمين وفرق محددين بالاسم. عند عدم التعيين أو الإيقاف: لا يمكن إنشاء أي رابط، ولا يُفتح أي رابط." + }, + "publicSharing.enabled": { + label: "مُفعَّل", + helpText: "السماح بروابط المشاركة لسجلات هذا الكائن. يُفحص عند كل استخدام: إيقافه يمنع كل الروابط الحالية من الفتح، وإعادة تشغيله تعيد خدمتها. عند الإيقاف (الافتراضي): لا يُطبَّق أي شيء آخر هنا." + }, + "publicSharing.allowedAudiences": { + label: "فئات الجمهور المستهدف المسموح بها", + helpText: "فئات الجمهور المستهدف التي يجوز للرابط الجديد تسميتها؛ وتُرفض أي فئة أخرى. عند عدم التعيين: الرابط فقط. تظل كل فئة بحاجة إلى الرابط نفسه: «مسجّل الدخول» يحتاج أيضًا إلى مستخدم مسجّل الدخول، و«البريد الإلكتروني» يحتاج أيضًا إلى أن يكون عنوان المستلم في قائمة الرابط." + }, + "publicSharing.allowedPermissions": { + label: "الأذونات المسموح بها", + helpText: "مستويات الأذونات التي يجوز للرابط الجديد منحها؛ ويُرفض أي مستوى آخر. عند عدم التعيين: العرض فقط." + }, + "publicSharing.maxExpiryDays": { + label: "الحد الأقصى لأيام الانتهاء", + helpText: "أبعد تاريخ انتهاء يجوز للرابط الجديد طلبه، بالأيام من الآن؛ ويُرفض ما بعده. عند عدم التعيين: 365. لا يفرض انتهاءً: فالرابط المُنشأ دون تاريخ انتهاء لا تنتهي صلاحيته أبدًا." + }, + "publicSharing.redactFields": { + label: "الحقول المحجوبة", + helpText: "أسماء حقول هذا الكائن التي تُزال من كل سجل يقدّمه رابط، أيًّا كان الجمهور المستهدف؛ ولا يتأثر وصول المالك نفسه. يُرفض عند النشر أي اسم ليس حقلًا من هذا الكائن." + }, + "publicSharing.eligibility": { + label: "الأهلية", + helpText: "شرط CEL على السجل (مثل record.status == 'published'): لا يُنشأ الرابط إلا ما دام صحيحًا (TRUE)، ويتوقف الرابط الموجود عن الفتح بمجرد أن يفقد سجله الأهلية. الشرط الذي لا يُترجَم أو يتعطل يرفض الرابط." + }, + managedBy: { + label: "جهة الإدارة", + helpText: "فئة دورة الحياة: platform (CRUD للمستخدم)، config (يؤلفه المسؤول)، system-data (مخطط تعرّفه المنصة مع بيانات قابلة للكتابة من المسؤول/المستخدم)، engine-owned (لا كتابة من المستخدمين)، append-only (تدقيق)، better-auth (الهوية). تشتق عملاء الواجهة منها إمكانات CRUD الخاصة بها، لذا فهي تحدد ما يُعرض على المستخدم في سجلات هذا الكائن." + }, + userActions: { + label: "إجراءات المستخدم", + helpText: "الإدخالات العامة (جديد، استيراد، تحرير، حذف، تصدير) التي تعرضها عملاء الواجهة على سجلات هذا الكائن، متجاوزةً الإعداد الافتراضي لـ managedBy إدخالًا بإدخال. الإدخال غير المعيَّن يُبقي ذلك الإعداد الافتراضي: platform يعرض الخمسة كلها؛ والقيمتان config، system-data تعرضانها كلها عدا الاستيراد؛ والقيم engine-owned، append-only، better-auth تعرض التصدير فقط. المفتاح الذي لم يُلمس لا يكتب شيئًا، لذا يظهر متوقفًا حتى حيث يعرض الإعداد الافتراضي الإدخال. في كائن engine-owned أو append-only، يتيح تشغيل إدخال للمستخدمين أيضًا إجراء تلك الكتابة عبر واجهة البيانات. ولا يزال المستخدمون بحاجة إلى الإذن المطابق." + }, + "userActions.create": { + label: "إنشاء", + helpText: "زر «جديد»: التشغيل يُظهره، والإيقاف يُخفيه. يُحرَّر كائن {enabled, visibleWhen, disabledWhen} المخزَّن مفتاحًا بمفتاح؛ اكتب واحدًا في المصدر لتقييد الزر بالسجل ضمن النطاق، ويُقيَّم مرة واحدة لكل شريط أدوات (السجل المضيف في القائمة المرتبطة)." + }, + "userActions.import": { + label: "استيراد", + helpText: "إدخال الاستيراد من CSV: التشغيل يُظهره، والإيقاف يُخفيه. يُحرَّر كائن {enabled, visibleWhen, disabledWhen} المخزَّن مفتاحًا بمفتاح؛ اكتب واحدًا في المصدر لتقييد الإدخال بالسجل ضمن النطاق، ويُقيَّم مرة واحدة لكل شريط أدوات." + }, + "userActions.edit": { + label: "تحرير", + helpText: "تحرير السجلات الموجودة، ضمن السطر وفي النموذج: التشغيل يتيحه، والإيقاف يُخفيه. يُحرَّر كائن {enabled, visibleWhen, disabledWhen} المخزَّن مفتاحًا بمفتاح؛ اكتب واحدًا في المصدر لتقييد كل صف بسجله." + }, + "userActions.delete": { + label: "حذف", + helpText: "حذف الصف والحذف الجماعي: التشغيل يتيحه، والإيقاف يُخفيه. يُحرَّر كائن {enabled, visibleWhen, disabledWhen} المخزَّن مفتاحًا بمفتاح؛ اكتب واحدًا في المصدر لتقييد كل صف بسجله." + }, + "userActions.exportCsv": { + label: "تصدير CSV", + helpText: "إدخال التصدير إلى CSV. عند عدم التعيين: يظهر، لأن كل فئات managedBy تتيح التصدير." + }, + editMode: { + label: "وضع التحرير", + helpText: "نية تفاعل التحرير لسجلات هذا الكائن. عند الغياب، يختار العارض إعداده الافتراضي. نية عابرة للعارضات، وليست تنسيقًا." + }, + fileAccessDelegate: { + label: "مفوَّض الوصول إلى الملفات", + helpText: "خدمة النواة التي تخوّل تنزيل الملفات المملوكة لحقول الوسائط في هذا الكائن، وتقرر ما إذا كان القارئ الذي لا يجوز له قراءة sys_file يرى اسمها وحجمها ونوعها عند قراءة سجل من هذا الكائن (يُسأل مرة واحدة لكل سجل مالك في كل قراءة من هذا النوع)، بدلًا من اختبار ما إذا كان المستدعي يستطيع قراءة الصف المالك. للكائنات التي يتوسط خدمةٌ الوصولَ إليها. يفشل مغلقًا." + }, + lifecycle: { + label: "دورة الحياة", + helpText: "عقد دورة حياة البيانات (ADR-0057): كم تعيش الصفوف وكيف تُستعاد المساحة. اتركه فارغًا لدلالات السجلات الدائمة. تتطلب الفئات غير السجلية سياسة تقييد واحدة على الأقل (الاحتفاظ، أو TTL، أو التدوير)." + }, + "lifecycle.class": { + label: "الفئة", + helpText: "عقد الاستمرارية لصفوف هذا الكائن" + }, + "lifecycle.retention": { + label: "الاحتفاظ", + helpText: "نافذة احتفاظ قائمة على العمر" + }, + "lifecycle.retention.maxAge": { + label: "الحد الأقصى للعمر", + helpText: "تُحصد الصفوف الأقدم من هذا (حسب created_at). قيمة مدة حرفية: h/d/w/y، مثل \"30d\"" + }, + "lifecycle.ttl": { + label: "مدة البقاء (TTL)", + helpText: "انتهاء صلاحية TTL لكل صف" + }, + "lifecycle.ttl.field": { + label: "الحقل", + helpText: "حقل الطابع الزمني الذي تُقاس منه مدة TTL (مثل expires_at)" + }, + "lifecycle.ttl.expireAfter": { + label: "الانتهاء بعد", + helpText: "تنتهي صلاحية الصفوف بعد هذه المدة من قيمة الحقل، مثل \"1d\"" + }, + "lifecycle.storage": { + label: "التخزين", + helpText: "تدوير فعلي لبيانات القياس عالية التكرار (SQLite: حذف شريحة بـ DROP بتعقيد O(1))" + }, + "lifecycle.storage.strategy": { + label: "الاستراتيجية", + helpText: "استراتيجية التخزين" + }, + "lifecycle.storage.shards": { + label: "الشرائح", + helpText: "الشرائح المحتفَظ بها؛ النافذة الإجمالية = الشرائح × الوحدة" + }, + "lifecycle.storage.unit": { + label: "الوحدة", + helpText: "العرض الزمني لشريحة واحدة" + }, + "lifecycle.archive": { + label: "الأرشفة", + helpText: "تسليم إلى التخزين البارد (فئة التدقيق). لا تُحذف الصفوف من التخزين الساخن أبدًا قبل نجاح نسخة الأرشيف." + }, + "lifecycle.archive.after": { + label: "بعد", + helpText: "أرشفة الصفوف الأقدم من هذا — يجب أن يساوي retention.maxAge" + }, + "lifecycle.archive.to": { + label: "إلى", + helpText: "اسم مصدر البيانات الهدف للتخزين البارد" + }, + "lifecycle.archive.keep": { + label: "الإبقاء", + helpText: "المدة التي يحتفظ فيها الأرشيف بالصفوف (فارغ = إلى الأبد)، مثل \"7y\"" + }, + "lifecycle.reclaim": { + label: "الاستعادة", + helpText: "استعادة مساحة المشغّل بعد عمليات المسح (مُفعَّلة افتراضيًا للفئات غير السجلية)" + } + } + }, + object_extension: { + label: "امتداد الكائن", + description: "الحقول والقواعد التي تضيفها حزمة إلى كائن تعرّفه حزمة أخرى" + }, + field: { + label: "الحقل", + sections: { + basics: { + label: "الأساسيات", + description: "الهوية الأساسية للحقل وقيوده." + }, + configuration: { + label: "التهيئة", + description: "إعدادات خاصة بنوع الحقل (تعتمد الكتل المرئية على النوع المختار)." + }, + formula: { + label: "الصيغ والقيم المحسوبة", + description: "القيم المحسوبة وملخّصات التجميع التراكمي." + }, + advanced: { + label: "متقدم", + description: "إعدادات قاعدة البيانات والواجهة والتدقيق والأمان." + } + }, + fields: { + name: { + label: "الاسم", + helpText: "معرّف فريد (snake_case، غير قابل للتغيير بعد الإنشاء)" + }, + label: { + label: "التسمية", + helpText: "اسم العرض للمستخدمين" + }, + type: { + label: "النوع", + helpText: "نوع بيانات هذا الحقل" + }, + group: { + label: "المجموعة", + helpText: "اسم المجموعة لتخطيط النموذج" + }, + description: { + label: "الوصف", + helpText: "نص المساعدة المعروض للمستخدمين" + }, + required: { + label: "مطلوب", + helpText: "يجب على المستخدم تقديم قيمة" + }, + unique: { + label: "فريد", + helpText: "لا يمكن أن يحمل سجلان القيمة نفسها" + }, + multiple: { + label: "متعدد", + helpText: "السماح بقيم متعددة (للاختيار/البحث)" + }, + defaultValue: { + label: "القيمة الافتراضية", + helpText: "القيمة الافتراضية للسجلات الجديدة" + }, + placeholder: { + label: "النص النائب", + helpText: "نص تلميح يظهر داخل حقل الإدخال الفارغ (ويختفي بمجرد إدخال قيمة)؛ استخدم inlineHelpText لمساعدة ظاهرة دائمًا" + }, + inlineHelpText: { + label: "نص المساعدة المضمَّن", + helpText: "مساعدة ظاهرة دائمًا تُعرض أسفل حقل الإدخال، بخلاف `placeholder` الذي يختفي بمجرد إدخال قيمة." + }, + minLength: { + label: "الحد الأدنى للطول", + helpText: "الحد الأدنى لطول الأحرف" + }, + maxLength: { + label: "الحد الأقصى للطول", + helpText: "الحد الأقصى لطول الأحرف" + }, + valueDomain: { + label: "نطاق القيم", + helpText: "المعيار الذي يجب أن تنتمي إليه القيمة المكتوبة: iana_time_zone، أو iso_4217_currency، أو iso_3166_alpha2. وتُرفض الكتابة التي تحمل قيمة من خارجه" + }, + rows: { + label: "الصفوف", + helpText: "ارتفاع المحرر المضمَّن بالصفوف النصية" + }, + min: { + label: "الحد الأدنى", + helpText: "أدنى قيمة" + }, + max: { + label: "الحد الأقصى", + helpText: "أقصى قيمة" + }, + precision: { + label: "الدقة", + helpText: "إجمالي الأرقام" + }, + scale: { + label: "المقياس", + helpText: "عدد الأرقام العشرية" + }, + useGrouping: { + label: "استخدام التجميع", + helpText: "تجميع الأرقام (فواصل الآلاف) في القيم المعروضة. عند عدم التعيين: يقرر العارض؛ والمفتاح الذي لم يُلمس لا يكتب شيئًا، لذا يظهر متوقفًا حتى حيث يجمّع العارض. عند الإيقاف: لا تجميع أبدًا، مثل سنة أو معرّف. عند التشغيل: تجميع دائمًا." + }, + percentDisplay: { + label: "عرض النسبة المئوية", + helpText: "كيف تُعرض القيمة للقراءة فقط، في خلايا القوائم وصفحة السجل. عند عدم التعيين: شريط تقدّم بجانب النسبة المئوية. الشريط: شريط التقدّم دائمًا، للتقدّم. عادي: النسبة المئوية وحدها، لمعدّل أو احتمال." + }, + currencyConfig: { + label: "تهيئة العملة", + helpText: "العملة التي يكون بها هذا الحقل — حقل عملة، أو صيغة نوع إرجاعها Currency. عند عدم التعيين: الوضع الديناميكي. القيمة رقم مجرّد في كل الأوضاع." + }, + "currencyConfig.currencyMode": { + label: "وضع العملة", + helpText: "dynamic (الافتراضي): ليست للحقل عملة خاصة به، وتُعرض المبالغ بالعملة الافتراضية للمستأجر (الإعداد localization.currency). fixed: للحقل عملة واحدة هي defaultCurrency. record: لكل سجل عملته الخاصة، المخزَّنة في الحقل الذي يسمّيه currencyField." + }, + "currencyConfig.currencyField": { + label: "حقل العملة", + helpText: "لوضع record فقط، ومطلوب فيه: الحقل في هذا الكائن الذي يحمل رمز ISO 4217 لكل سجل — حقل نصي بـ valueDomain قيمته iso_4217_currency، ويُسمّى عُرفًا currency_code." + }, + "currencyConfig.defaultCurrency": { + label: "العملة الافتراضية", + helpText: "العملة الوحيدة لحقل بالوضع fixed، كرمز ISO 4217 من ثلاثة أحرف (مثل USD، EUR). مطلوبة في الوضع fixed؛ ولا يملؤها شيء تلقائيًا. لا تُقرأ في الوضع dynamic؛ وتُرفض في الوضع record." + }, + step: { + label: "الخطوة", + helpText: "مقدار الخطوة لشريط التمرير (الافتراضي 1). للعارض فقط: لا يرفض مسار الكتابة قيمة خارج شبكة الخطوات." + }, + maxSize: { + label: "الحد الأقصى للحجم", + helpText: "الحد الأقصى المسموح لحجم الملف بالبايت (عدد صحيح موجب). يُفرض من جهة الخادم عند الكتابة مقابل حجم الملف المخزَّن — والملف الذي لم يُسجَّل حجمه لا يمكن أن يفشل فيه." + }, + accept: { + label: "الأنواع المقبولة", + helpText: "أنواع الرفع المسموح بها، كأنواع MIME أو أحرف بدل type/* أو لواحق .ext ‏(مثل image/*، .pdf). تُعرض على منتقي الملفات ويُعاد فحصها من جهة الخادم عند الكتابة مقابل الملف المخزَّن. عند عدم التعيين: أي نوع." + }, + dimensions: { + label: "الأبعاد", + helpText: "عدد أبعاد المتجه — عدد صحيح من 1 إلى 10000 (مثل 1536 لتضمينات OpenAI)." + }, + language: { + label: "اللغة", + helpText: "لغة المحرر لتلوين الصيغة (مثل javascript، python، sql)." + }, + autonumberFormat: { + label: "تنسيق الترقيم التلقائي", + helpText: "نص حرفي مع عدّاد {0000}، ورموز التاريخ {YYYY}/{MM}/{DD}/{YYYYMMDD} بالمنطقة الزمنية للأعمال، والاستبدال بـ {field_name}. يُعاد تعيين العدّاد لكل بادئة معروضة. عند إغفاله في حقل ترقيم تلقائي تكون قيمته الافتراضية {0000}." + }, + referenceVia: { + label: "المرجع عبر", + helpText: "يجعل هذا الحقل النصي نصف المعرّف في مؤشر متعدد الأشكال: يسمّي الحقل الشقيق في الكائن نفسه الذي يحمل اسم الكائن الهدف، لكل صف (ADR-0052 §5). بصيغة snake_case؛ للحقول النصية فقط، ولا يجتمع مع `reference`." + }, + referenceDeletedWhen: { + label: "المرجع محذوف عندما", + helpText: "بجانب referenceVia فقط: في الصفوف التي يحمل فيها حقل اختيار شقيق أحد خياراته، يكون السجل الذي يسمّيه هذا المؤشر قد حذفته المنصة، ويُعرض المؤشر كشاهد محذوف بتسمية ذلك الخيار. عند عدم التعيين: لا يُعلَّم أي صف كمحذوف." + }, + "referenceDeletedWhen.field": { + label: "الحقل", + helpText: "حقل الاختيار الشقيق في هذا الكائن (snake_case)، مثل cancel_reason. يجب أن يكون اختيارًا بقيمة واحدة يُعلن خياراته ضمنيًا." + }, + "referenceDeletedWhen.value": { + label: "القيمة", + helpText: "قيمة الخيار في حقل الاختيار ذاك التي تعني أن الهدف قد حُذف، مثل record_deleted. يجب أن تكون إحدى قيم الخيارات المُعلنة فيه." + }, + mailAssociation: { + label: "مطابقة البريد الوارد", + helpText: "يربط البريد الوارد بسجلات هذا الكائن التي تطابق قيمتها في هذا الحقل مُرسِل الرسالة أو أحد مستلميها (دون تمييز حالة الأحرف؛ مع استبعاد عنوان صندوق البريد نفسه). لحقول البريد الإلكتروني فقط. لا يسري إلا حيث تكون خدمة صندوق بريد مُعدّة." + }, + dueLike: { + label: "الموعد النهائي", + helpText: "يعلّم هذا التاريخ كموعد نهائي: بمجرد انقضائه، يجوز لطرق العرض إظهاره كمتأخر (صياغة نسبية ولون للتأخر). عند الإيقاف أو عدم التعيين: ليس موعدًا نهائيًا، ولا يُستنتج شيء من اسم الحقل." + }, + settledWhen: { + label: "مُسوّى عندما", + helpText: "شرط CEL على السجل (مثل record.status == 'done') — ما دام صحيحًا (TRUE) يكون الموعد النهائي مُسوّى، ولا تنطبق على ذلك السجل أي صياغة أو لون للتأخر. للعرض فقط." + }, + options: { + label: "الخيارات", + helpText: "الخيارات المتاحة (أزواج التسمية/القيمة)" + }, + "options.label": { + label: "التسمية" + }, + "options.value": { + label: "القيمة" + }, + "options.description": { + label: "الوصف" + }, + "options.color": { + label: "اللون" + }, + "options.default": { + label: "الافتراضي" + }, + "options.visibleWhen": { + label: "مرئي عندما" + }, + "options.kind": { + label: "النوع" + }, + reference: { + label: "المرجع", + helpText: "اسم الكائن المُشار إليه" + }, + referenceNameVisible: { + label: "إظهار الاسم عند تعذّر القراءة", + helpText: "يتيح لقارئ هذا السجل رؤية اسم السجل المُشار إليه، كنص عادي بلا رابط، حتى حين لا يجوز له فتح ذلك السجل. لا يُعرض منه شيء آخر، وتظل أي قاعدة على مستوى الحقل تخص الاسم سارية (فيُقرأ الاسم حينها «سجل #» مع المعرّف). عند عدم التعيين: يظهر مثل هذا المرجع كسجل مقيَّد." + }, + deleteBehavior: { + label: "سلوك الحذف", + helpText: "ما يحدث عند حذف السجل المُشار إليه" + }, + relatedListFilter: { + label: "عامل تصفية القائمة المرتبطة", + helpText: "عامل التصفية الافتراضي للقائمة المرتبطة الخاصة بهذه العلاقة في صفحة تفاصيل السجل الأصل — يُركَّب بـ AND مع مطابقة السجل الأصل، وتعدّ شارة علامة التبويب المجموعة نفسها" + }, + displayField: { + label: "حقل العرض", + helpText: "الحقل المعروض كتسمية لكل مرشح في المنتقي. عند إغفاله، يُستخدم حقل العنوان الخاص بالكائن المُشار إليه." + }, + descriptionField: { + label: "حقل الوصف", + helpText: "حقل ثانوي يُعرض تحت التسمية في نافذة الاختيار السريع المنبثقة." + }, + allowCreate: { + label: "السماح بالإنشاء", + helpText: "السماح للمستخدم بإنشاء سجل من النص المكتوب عندما لا يجد المنتقي أي تطابق. الأنسب للكائنات التي يكون حقلها المطلوب الوحيد هو حقل العرض." + }, + lookupPageSize: { + label: "حجم صفحة البحث", + helpText: "عدد الصفوف في كل صفحة في مربع حوار منتقي السجلات — عدد صحيح موجب؛ الافتراضي 10." + }, + lookupColumns: { + label: "أعمدة البحث", + helpText: "أعمدة جدول منتقي السجلات: أسماء حقول الكائن المُشار إليه، أو إدخالات {field, label, width, type} ‏(مثل [\"name\", {\"field\": \"status\", \"label\": \"Stage\"}]). عند عدم التعيين: تُشتق من الكائن المُشار إليه." + }, + lookupFilters: { + label: "عوامل تصفية البحث", + helpText: "عامل تصفية أساسي على مرشحي المنتقي، كقواعد {field, operator, value} على الكائن المُشار إليه — operator واحد من eq، ne، gt، lt، gte، lte، contains، in، notIn ‏(مثل [{\"field\": \"status\", \"operator\": \"eq\", \"value\": \"active\"}]). يُطبَّق على كل واجهات المنتقي، ويُركَّب بـ AND مع أي عامل تصفية dependsOn." + }, + dependsOn: { + label: "يعتمد على", + helpText: "حقول في السجل نفسه تعتمد عليها خيارات هذا الحقل: يحجز النموذج هذا الحقل إلى أن يُعيَّن كل منها، ويعيد تقييمه عند تغيّر أحدها. يصفّي حقل البحث مرشحيه بها — الاسم يصفّي الحقل المماثل في الاسم في الكائن المُشار إليه، و{field, param} يسمّي حقلًا مختلفًا. في حقل الخيارات، اذكر أسماء الحقول الأصل؛ أما القاعدة الخاصة بكل خيار فتقع في visibleWhen لكل خيار." + }, + relatedListTitle: { + label: "عنوان القائمة المرتبطة", + helpText: "عنوان القائمة المرتبطة الخاصة بهذه العلاقة في صفحة تفاصيل السجل الأصل." + }, + relatedListColumns: { + label: "أعمدة القائمة المرتبطة", + helpText: "أعمدة القائمة المرتبطة الخاصة بهذه العلاقة في صفحة تفاصيل السجل الأصل، كأسماء حقول من هذا الكائن (الفرعي)، مثل name، status. عند عدم التعيين: تُشتق من الكائن الفرعي. أسماء فقط — التسميات وأنواع الخلايا والتنسيق تأتي من تعريفات حقول الكائن الفرعي." + }, + inlineTitle: { + label: "العنوان المضمَّن", + helpText: "عنوان الشبكة المضمَّنة الرئيسية-التفصيلية في السجل الأصل." + }, + inlineColumns: { + label: "الأعمدة المضمَّنة", + helpText: "أعمدة الشبكة المضمَّنة في نموذج السجل الأصل، بترتيب العرض؛ لا تُستخدم إلا عندما يعيّن هذا الحقل inlineEdit، الذي يُكتب في المصدر. عند عدم التعيين: تُشتق من الحقول القابلة للتحرير في هذا الكائن، وما زاد على ستة يبدأ في منتقي أعمدة الشبكة. الإدخال الذي لا يسمّي إلا حقلًا يأخذ نوعه وخياراته وقواعده من ذلك الحقل؛ أما مفاتيح الأعمدة الأخرى، بدءًا بـ type، فتُكتب في المصدر." + }, + "inlineColumns.name": { + label: "الاسم", + helpText: "حقل هذا الكائن (الفرعي) الذي يعرضه العمود ويحرّره (مثل quantity). لا شيء يتحقق منه عند الحفظ أو النشر: الاسم الذي ليس حقلًا من هذا الكائن يُعرض عمودًا نصيًا عاديًا." + }, + "inlineColumns.label": { + label: "التسمية", + helpText: "ترويسة العمود. عند عدم التعيين: تسمية الحقل نفسه." + }, + "inlineColumns.width": { + label: "العرض", + helpText: "عرض ثابت للعمود بالبكسل. عند عدم التعيين: يُحدَّد الحجم حسب نوع الخلية، فتتمدد الأعمدة النصية وتبقى أعمدة الأرقام والتواريخ والاختيار ضيقة." + }, + "inlineColumns.defaultHidden": { + label: "مخفي افتراضيًا", + helpText: "بدء العمود في منتقي أعمدة الشبكة بدلًا من الشاشة؛ ويمكن للمستخدم إظهاره. العمود الذي يكون حقله مطلوبًا يظهر دائمًا." + }, + inlineAmountField: { + label: "حقل المبلغ المضمَّن", + helpText: "حقل رقمي فرعي يُجمع لإجمالي الشبكة المضمَّنة." + }, + expression: { + label: "التعبير", + helpText: "تعبير CEL الذي تُحسب منه قيمة الحقل. في حقل الصيغة: يُحسب عند القراءة، بلا عمود مخزَّن. في أي حقل آخر: يحسبه المحرك من الصف الذي تخزّنه كل عملية كتابة، ويُخزَّن، فلا تثبت أبدًا قيمة يرسلها العميل." + }, + returnType: { + label: "نوع الإرجاع", + helpText: "نوع القيمة المُعلَن للصيغة، يُختم من نوع CEL المُستنتَج. يقرؤه المستهلكون بدلًا من إعادة تحليل التعبير. Currency: مبلغ مالي، بالعملة التي تعلنها تهيئة العملة لهذا الحقل (dynamic عند عدم التعيين)." + }, + summaryOperations: { + label: "عمليات التلخيص", + helpText: "تهيئة ملخّص التجميع التراكمي (لعلاقات الأصل-الفرع)" + }, + "summaryOperations.object": { + label: "الكائن", + helpText: "الكائن الفرعي المراد تجميعه" + }, + "summaryOperations.function": { + label: "الدالة", + helpText: "دالة التجميع" + }, + "summaryOperations.field": { + label: "الحقل", + helpText: "الحقل الفرعي المراد تجميعه (يُتجاهل في count)" + }, + "summaryOperations.relationshipField": { + label: "حقل العلاقة", + helpText: "المفتاح الأجنبي في الكائن الفرعي العائد إلى هذا الأصل (يُكتشف تلقائيًا عند إغفاله)" + }, + "summaryOperations.filter": { + label: "عامل التصفية", + helpText: "لا تُجمَّع إلا الصفوف الفرعية المطابقة لهذا الشرط (مثل status == received)" + }, + storage: { + label: "التخزين", + helpText: "قيود التخزين الفعلي (ADR-0113): تعريفات DDL التي لا يقتضيها عقد الكتابة عمدًا. عند عدم التعيين: لا شيء مطلوب." + }, + "storage.notNull": { + label: "غير فارغ (NOT NULL)", + helpText: "إصدار قيد NOT NULL في قاعدة البيانات على العمود. عند عدم التعيين، يبقى العمود قابلًا للفراغ حتى مع required — فالمحرك يفرض required عند الكتابة. إعلانه فوق صفوف فارغة موجودة ترحيل هدّام يحكمه انحراف المخطط (املأ البيانات أولًا). يُرفض بجانب requiredWhen." + }, + readonly: { + label: "للقراءة فقط", + helpText: "الحقل للقراءة فقط في النماذج" + }, + hidden: { + label: "مخفي", + helpText: "إخفاء الحقل من طرق عرض الواجهة الافتراضية" + }, + searchable: { + label: "قابل للبحث", + helpText: "تضمينه في نتائج البحث العام" + }, + sortable: { + label: "قابل للفرز", + helpText: "السماح بفرز القوائم حسب هذا الحقل" + }, + visibleWhen: { + label: "مرئي عندما", + helpText: "شرط CEL على السجل (مثل record.type == 'invoice') — لا يعرض النموذج هذا الحقل إلا ما دام صحيحًا (TRUE)." + }, + readonlyWhen: { + label: "للقراءة فقط عندما", + helpText: "شرط CEL على السجل (مثل record.status == 'paid') — يكون الحقل للقراءة فقط ما دام صحيحًا (TRUE)، ويُفرض من جهة الخادم: يُسقَط تغيير التحديث على حقل مقفل وتُحفظ القيمة المخزَّنة. يقرأ أعمدة السجل نفسه؛ ويرفض objectstack validate القراءة عبر حقل مرجعي." + }, + requiredWhen: { + label: "مطلوب عندما", + helpText: "شرط CEL على السجل — يكون الحقل مطلوبًا ما دام صحيحًا (TRUE)، ويُفرض من جهة الخادم كبوابة انتقال: تُرفض الكتابة التي تترك القيمة مفقودة إذا كان السجل ممتثلًا قبلها، لذا يظل الصف الذي تنقصه القيمة أصلًا يجتاز التعديلات غير المتعلقة. لقاعدة يجب أن تستوفيها كل كتابة، استخدم قاعدة script في validations. يُرفض بجانب storage.notNull." + }, + maskingRule: { + label: "قاعدة الإخفاء", + helpText: "إخفاء جزئي: إعداد مسبق ('phone'، 'id_card'، 'bank_account'، 'email'، 'name') أو {\"keepHead\": n, \"keepTail\": m}. تُخفى القيمة عن المستدعين الذين لا يحملون requiredPermissions الخاصة بهذا الحقل" + }, + requiredPermissions: { + label: "الأذونات المطلوبة", + helpText: "القدرات (systemPermissions في مجموعة الأذونات) التي يجب أن يحملها المستدعي — كل واحدة مُدرجة — لقراءة هذا الحقل أو تحريره (ADR-0066 D3). من دونها تُخفى القيمة عند القراءة (جزئيًا عند تعيين maskingRule) وتُرفض التعديلات. عند الفراغ أو عدم التعيين: لا قيد على القدرات." + }, + internal: { + label: "داخلي", + helpText: "عدم إعادة قيمة هذا الحقل أبدًا على مسار البيانات العام: يحذف المحرك المفتاح من نتائج find/findOne ومن متون استجابات الإنشاء والتحديث، في الإسقاط الافتراضي وعندما يسمّي العميل الحقل في ?select=. لا يتأثر التخزين ولا التصفية ولا الفهرسة." + }, + trackHistory: { + label: "تتبّع السجل", + helpText: "عرض تغييرات قيمة هذا الحقل كإدخالات في الخط الزمني لنشاط السجل (ADR-0052 §5b). اشتراك اختياري لكل حقل." + }, + widget: { + label: "عنصر الواجهة", + helpText: "تجاوز عنصر واجهة النموذج — يسمّي مكوّن حقل مسجَّلًا، يُبحث عنه بالصيغة `field:` متبوعة بهذا الاسم، لعرض الحقل بدلًا من العارض الافتراضي للنوع. الاسم غير المسجَّل يتراجع إلى عارض النوع." + }, + ackPlaintextMasking: { + label: "الإقرار بالنص الصريح أثناء التخزين", + helpText: "تأكيد أن عقد حقل كلمة المرور العام هذا — نص صريح أثناء التخزين ومُخفى عند القراءة — مقصود، مما يُسكت التحذير وقت التأليف (ADR-0100). لا تأثير له على أي نوع آخر." + } + } + }, + hook: { + label: "الخطاف", + sections: { + identity: { + label: "الهوية", + description: "ماهية هذا الخطاف ومتى يُطلَق." + }, + body: { + label: "المتن", + description: "تعبير مضمَّن أو JavaScript معزول يُنفَّذ عند إطلاق الخطاف." + }, + legacy_handler: { + label: "المعالج القديم", + description: "مرجع اسم دالة — مُهمَل لصالح body." + }, + execution: { + label: "التنفيذ" + } + }, + fields: { + name: { + label: "الاسم", + helpText: "معرّف بصيغة snake_case (غير قابل للتغيير بعد الإنشاء)" + }, + label: { + label: "التسمية" + }, + description: { + label: "الوصف" + }, + object: { + label: "الكائن", + helpText: "اسم الكائن الهدف (أو \"*\" للنطاق العام)" + }, + events: { + label: "الأحداث", + helpText: "أحداث دورة الحياة (مثل beforeInsert، afterUpdate)" + }, + priority: { + label: "الأولوية", + helpText: "الأرقام الأصغر تُنفَّذ أولًا" + }, + body: { + label: "المتن", + helpText: "إما تعبير L1 أو متن JS معزول من المستوى L2" + }, + "body.language": { + label: "اللغة", + helpText: "expression = صيغة خالصة؛ js = JavaScript معزول" + }, + "body.source": { + label: "المصدر", + helpText: "مصدر متن الدالة — بلا عمليات استيراد على المستوى الأعلى" + }, + "body.capabilities": { + label: "القدرات", + helpText: "واجهات ctx المسموح بها (api.read، api.write، crypto.uuid، log، …)" + }, + "body.timeoutMs": { + label: "المهلة (مللي ثانية)", + helpText: "المهلة لكل استدعاء (مللي ثانية)" + }, + "body.memoryMb": { + label: "الذاكرة (ميغابايت)", + helpText: "حد الذاكرة لكل استدعاء (ميغابايت، بحد أقصى 256)" + }, + refusals: { + label: "حالات الرفض", + helpText: "فهرس حالات الرفض، مفهرس برمز SCREAMING_SNAKE الذي يرميه المتن (مثل CLEAN_VERSION_REQUIRED). يقرأ المستدعي الرسالة المُعلنة، مترجمة إلى لغته، مع الحالة المُعلنة. لمتون JavaScript ‏(L2) فقط" + }, + "refusals.message": { + label: "الرسالة", + helpText: "الجملة التي يقرؤها المستخدم عندما يرمي المتن هذا الرمز — نص عادي، بلا عناصر نائبة" + }, + "refusals.status": { + label: "الحالة", + helpText: "حالة HTTP للرفض (400–499). عند الغياب: 400" + }, + handler: { + label: "المعالج", + helpText: "اسم دالة المعالج (مُهمَل — يُفضَّل `body`)" + }, + async: { + label: "غير متزامن", + helpText: "التشغيل في الخلفية، دون حجب المعاملة" + }, + onError: { + label: "عند الخطأ" + }, + timeoutMs: { + label: "المهلة (مللي ثانية)", + helpText: "إيقاف الخطاف بعد N مللي ثانية" + }, + runAs: { + label: "التشغيل بهوية", + helpText: "الهوية لعمليات بيانات ctx.api: وراثة الكتابة المُطلِقة (الافتراضي)، أو system (مرتفعة الصلاحيات)، أو user (المستخدم المُطلِق)" + }, + condition: { + label: "الشرط", + helpText: "شرط CEL — لا يُنفَّذ الخطاف إلا عندما يكون صحيحًا (TRUE)" + }, + retryPolicy: { + label: "سياسة إعادة المحاولة", + helpText: "إعادة المحاولة عند الفشل — أنفع ما تكون للخطافات غير المتزامنة" + }, + "retryPolicy.maxRetries": { + label: "الحد الأقصى لإعادة المحاولات", + helpText: "الحد الأقصى لمحاولات الإعادة" + }, + "retryPolicy.backoffMs": { + label: "التراجع (مللي ثانية)", + helpText: "التأخير بين إعادة المحاولات (مللي ثانية)" + } + } + }, + seed: { + label: "البيانات الأولية", + description: "بيانات تجهيز / تهيئة تُطبَّق عند النشر" + }, + picklist: { + label: "قائمة الاختيار", + description: "قائمة خيارات مشتركة تشير إليها حقول الاختيار بالاسم" + }, + mapping: { + label: "تعيين الاستيراد", + description: "تعيين حقول قابل لإعادة الاستخدام للاستيراد/التصدير (إعادة تسمية + تحويلات)، يُشار إليه بالاسم عند الاستيراد" + }, + view: { + label: "طريقة العرض", + sections: { + basics: { + label: "الأساسيات", + description: "الهوية والواجهة الأساسية." + }, + columns_filters: { + label: "الأعمدة وعوامل التصفية", + description: "ما تعرضه الصفوف وكيف يصفّيها المستخدمون." + }, + table_options: { + label: "خيارات الجدول", + description: "خيارات عرض خاصة بالشبكة فقط." + }, + pagination: { + label: "ترقيم الصفحات", + description: "حجم الصفحة وخيارات حجم الصفحة — يقبلها كل نوع من أنواع طرق العرض، لا الشبكات فقط." + }, + kanban: { + label: "كانبان", + description: "تهيئة لوحة خاصة بكانبان." + }, + calendar: { + label: "التقويم", + description: "تهيئة خاصة بالتقويم." + }, + gantt: { + label: "مخطط جانت", + description: "تهيئة خاصة بمخطط جانت." + }, + gallery: { + label: "المعرض", + description: "تهيئة خاصة بالمعرض." + }, + timeline: { + label: "الخط الزمني", + description: "تهيئة خاصة بالخط الزمني." + }, + chart: { + label: "المخطط البياني", + description: "تهيئة خاصة بالمخطط البياني." + }, + end_user_controls: { + label: "عناصر تحكم المستخدم النهائي", + description: "ما يستطيع المستخدمون النهائيون فعله في طريقة العرض هذه — عوامل التصفية السريعة، وعلامات تبويب التصفية، والتبديل بين التصورات." + }, + navigation_sharing: { + label: "التنقل والمشاركة", + description: "أين تظهر طريقة العرض هذه ومن يستطيع رؤيتها." + } + }, + fields: { + name: { + label: "الاسم", + helpText: "بصيغة snake_case، فريد لكل بيئة" + }, + label: { + label: "التسمية" + }, + description: { + label: "الوصف" + }, + type: { + label: "النوع", + helpText: "واجهة العرض الأساسية" + }, + data: { + label: "البيانات", + helpText: "مصدر البيانات — مثل {\"provider\":\"object\",\"object\":\"task\"}" + }, + columns: { + label: "الأعمدة", + helpText: "الأعمدة المراد عرضها (أسماء حقول من الكائن المحدد)" + }, + "columns.field": { + label: "الحقل" + }, + "columns.label": { + label: "التسمية" + }, + "columns.width": { + label: "العرض (بكسل)" + }, + "columns.align": { + label: "المحاذاة" + }, + "columns.hidden": { + label: "مخفي" + }, + "columns.sortable": { + label: "قابل للفرز" + }, + "columns.resizable": { + label: "قابل لتغيير الحجم" + }, + "columns.wrap": { + label: "التفاف النص" + }, + "columns.type": { + label: "نوع العارض" + }, + "columns.pinned": { + label: "مثبَّت" + }, + "columns.summary": { + label: "الملخّص" + }, + "columns.prefix": { + label: "البادئة" + }, + "columns.link": { + label: "الرابط الأساسي" + }, + "columns.action": { + label: "إجراء النقر" + }, + filter: { + label: "عامل التصفية", + helpText: "شروط التصفية — منشئ مرئي مماثل لشريط أدوات القائمة، مع عوامل ومدخلات قيم تراعي نوع الحقل" + }, + sort: { + label: "الفرز", + helpText: "ترتيب الفرز الافتراضي" + }, + "sort.field": { + label: "الحقل" + }, + "sort.order": { + label: "الاتجاه" + }, + searchableFields: { + label: "الحقول القابلة للبحث", + helpText: "أسماء الحقول المتاحة للبحث السريع" + }, + resizable: { + label: "قابل لتغيير الحجم", + helpText: "تفعيل تغيير حجم الأعمدة" + }, + compactToolbar: { + label: "شريط أدوات مضغوط", + helpText: "طيّ التجميع/اللون/الكثافة/إخفاء الحقول في نافذة منبثقة واحدة لإعدادات طريقة العرض" + }, + rowHeight: { + label: "ارتفاع الصف", + helpText: "إعداد ارتفاع الصف / الكثافة" + }, + selection: { + label: "التحديد", + helpText: "تهيئة تحديد الصفوف" + }, + "selection.type": { + label: "النوع", + helpText: "وضع التحديد" + }, + pagination: { + label: "ترقيم الصفحات", + helpText: "تهيئة ترقيم الصفحات" + }, + "pagination.pageSize": { + label: "حجم الصفحة", + helpText: "عدد السجلات في كل صفحة. في طريقة عرض بلا متصفح صفحات (kanban، gallery، timeline) يكون هو سقف الجلب، ويلتزم العارض بأمرين: أن يقيّد جلبه بهذا العدد، وأن يُظهر — عندما تكون المجموعة المصفّاة أكبر منه — إشارة اقتطاع مرئية تقول إن ما على الشاشة ليس المجموعة كلها" + }, + "pagination.pageSizeOptions": { + label: "خيارات حجم الصفحة", + helpText: "خيارات حجم الصفحة المتاحة" + }, + kanban: { + label: "كانبان" + }, + calendar: { + label: "التقويم" + }, + gantt: { + label: "مخطط جانت" + }, + gallery: { + label: "المعرض" + }, + timeline: { + label: "الخط الزمني" + }, + chart: { + label: "المخطط البياني" + }, + userFilters: { + label: "عوامل تصفية المستخدم", + helpText: "شريط التصفية السريعة: نمط العنصر (dropdown / tabs / toggle) + الحقول المكشوفة أو إعدادات علامات التبويب المسبقة" + }, + "userFilters.element": { + label: "العنصر", + helpText: "نمط عنصر تحكم التصفية في طرق عرض الكائنات: \"dropdown\" (رقائق قيم لكل حقل). \"toggle\" مُهمَل. \"tabs\" للصفحات فقط — استخدم `listViews` للإعدادات المسبقة المُسمّاة." + }, + "userFilters.fields": { + label: "الحقول", + helpText: "الحقول المكشوفة كعوامل تصفية سريعة (عناصر dropdown/toggle)" + }, + "userFilters.fields.field": { + label: "الحقل" + }, + "userFilters.fields.label": { + label: "التسمية" + }, + "userFilters.fields.type": { + label: "النوع" + }, + "userFilters.fields.options": { + label: "الخيارات" + }, + "userFilters.fields.showCount": { + label: "إظهار العدد" + }, + "userFilters.fields.defaultValues": { + label: "القيم الافتراضية" + }, + appearance: { + label: "المظهر", + helpText: "allowedVisualizations: العارضات التي يجوز للمستخدمين التبديل بينها" + }, + "appearance.showDescription": { + label: "إظهار الوصف", + helpText: "إظهار نص وصف طريقة العرض" + }, + "appearance.allowedVisualizations": { + label: "التصورات المسموح بها", + helpText: "قائمة مسموحة بأنواع التصورات التي يمكن للمستخدمين التبديل بينها (مثل [\"grid\", \"gallery\", \"kanban\"])" + }, + userActions: { + label: "إجراءات المستخدم", + helpText: "مفاتيح شريط الأدوات: الفرز / البحث / التصفية / ارتفاع الصف" + }, + "userActions.sort": { + label: "الفرز", + helpText: "السماح للمستخدمين بفرز السجلات" + }, + "userActions.search": { + label: "البحث", + helpText: "السماح للمستخدمين بالبحث في السجلات" + }, + "userActions.filter": { + label: "عامل التصفية", + helpText: "السماح للمستخدمين بتصفية السجلات" + }, + "userActions.refresh": { + label: "التحديث", + helpText: "السماح للمستخدمين بإعادة تحميل بيانات طريقة العرض من الخادم دون إعادة تحميل الصفحة بالكامل" + }, + "userActions.rowHeight": { + label: "ارتفاع الصف", + helpText: "السماح للمستخدمين بتبديل ارتفاع الصف/الكثافة" + }, + "userActions.group": { + label: "المجموعة", + helpText: "السماح للمستخدمين بتغيير تجميع السجلات من شريط الأدوات. مفتاح تبديل فقط — يُهيَّأ التجميع نفسه في كتلة `grouping` على مستوى طريقة العرض." + }, + "userActions.addRecordForm": { + label: "نموذج إضافة السجل", + helpText: "إضافة السجلات عبر نموذج بدلًا من الإضافة ضمن السطر" + }, + "userActions.editInline": { + label: "التحرير ضمن السطر", + helpText: "السماح للمستخدمين بتحرير السجلات ضمن السطر. مُفعَّل افتراضيًا: المستخدم الذي يجوز له تحديث الكائن يحرّر الخلية في مكانها بعنصر الواجهة المراعي لنوع الحقل (عنصر التحكم نفسه الذي يستخدمه النموذج)، تحت بوابة الأذونات التي تقرر أصلًا ما إذا كان الكائن قابلًا للتحرير. أعلِن `editInline: false` لجعل القائمة للقراءة فقط في مكانها." + }, + "userActions.hideFields": { + label: "إخفاء الحقول", + helpText: "السماح للمستخدمين بإخفاء الحقول/إظهارها من شريط الأدوات (الإمكانية الكامنة وراء قائمة `hiddenFields` على مستوى طريقة العرض). مفتاح منطقي — يختلف عن `hideFields` في مكوّن تفاصيل السجل، وهو مصفوفة بأسماء الحقول المراد حذفها. مُفعَّل افتراضيًا: يُخفي القارئ الأعمدة ويُظهرها لنفسه؛ أعلِن `hideFields: false` لحجب عنصر التحكم." + }, + "userActions.rowColor": { + label: "لون الصف", + helpText: "السماح للمستخدمين بتهيئة تلوين الصفوف من شريط الأدوات. مفتاح منطقي — تقع قواعد الألوان نفسها في كتلة `rowColor` على مستوى طريقة العرض. متوقف افتراضيًا: تلوين الصفوف اشتراك اختياري." + }, + "userActions.buttons": { + label: "الأزرار", + helpText: "معرّفات أزرار الإجراءات المخصّصة المراد إظهارها في شريط الأدوات" + }, + addRecord: { + label: "إضافة سجل", + helpText: "تهيئة نقطة الدخول لإضافة السجل" + }, + "addRecord.enabled": { + label: "مُفعَّل", + helpText: "إظهار نقطة الدخول لإضافة السجل — مطلوب: `true` يرسم زر الإضافة، أما `false` فيعلنه متوقفًا" + }, + "addRecord.position": { + label: "الموضع", + helpText: "موضع زر إضافة السجل: `top` (شريط الأدوات، الافتراضي)، أو `bottom` (تحت القائمة)، أو `both`" + }, + showRecordCount: { + label: "إظهار عدد السجلات", + helpText: "إظهار عدد السجلات أسفل القائمة" + }, + navigation: { + label: "التنقل", + helpText: "تهيئة التنقل عند النقر على عنصر (درج جانبي افتراضيًا، أو page، أو new_window، أو none)" + }, + "navigation.mode": { + label: "الوضع" + }, + "navigation.preventNavigation": { + label: "منع التنقل", + helpText: "تعطيل التنقل القياسي كليًا" + }, + "navigation.openNewTab": { + label: "الفتح في علامة تبويب جديدة", + helpText: "فرض الفتح في علامة تبويب جديدة (ينطبق على وضع page)" + }, + "navigation.size": { + label: "الحجم", + helpText: "فئة حجم الطبقة العلوية لتفاصيل الدرج الجانبي: 'auto' (الافتراضي — يشتقه العارض من عدد الحقول ومساحة العرض؛ ولا يكتب الذكاء الاصطناعي شيئًا) أو تجاوز تقريبي sm/md/lg/xl/full. فضّله على `width` بالبكسل؛ ويتجاهله وضع page." + }, + "navigation.width": { + label: "العرض", + helpText: "[مُهمَل ← size] عرض الدرج الجانبي بالبكسل/النسبة المئوية (مثل \"600px\"). لا يمكن اختيار عرض بالبكسل وقت التأليف دون معرفة مساحة عرض العميل — استخدم فئة `size`." + }, + sharing: { + label: "المشاركة", + helpText: "تهيئة مشاركة طريقة العرض: من قفل تهيئة طريقة العرض" + }, + "sharing.lockedBy": { + label: "مقفل بواسطة", + helpText: "المستخدم الذي قفل تهيئة طريقة العرض" + } + } + }, + page: { + label: "الصفحة", + sections: { + basics: { + label: "الأساسيات", + description: "هوية الصفحة والقالب." + }, + data_context: { + label: "سياق البيانات", + description: "ربط السجل والحالة المحلية للصفحة." + }, + layout: { + label: "التخطيط", + description: "مناطق الصفحة والمكوّنات الموضوعة فيها." + }, + interface: { + label: "الواجهة (صفحات القوائم)", + description: "وضع الواجهة (مماثل لـ Airtable): تعرّف الصفحة سطح بياناتها مباشرة — الأعمدة وعوامل التصفية والتصورات وشريط الأدوات — دون وراثة من طريقة عرض منفصلة." + }, + advanced: { + label: "متقدم", + description: "التفعيل والجمهور وإمكانية الوصول." + } + }, + fields: { + name: { + label: "الاسم", + helpText: "معرّف فريد (snake_case)" + }, + label: { + label: "التسمية", + helpText: "عنوان الصفحة المعروض للمستخدمين" + }, + icon: { + label: "الأيقونة", + helpText: "أيقونة قائمة التنقل" + }, + type: { + label: "النوع", + helpText: "نوع الصفحة. «قائمة / واجهة» تربط طريقة عرض مصدرية بسطح منسَّق — أما شكلها (شبكة / كانبان / تقويم / …) فهو تصوّر يُضبط ضمن «الواجهة»، وليس نوع صفحة." + }, + template: { + label: "القالب", + helpText: "قالب التخطيط (مثل \"header-sidebar-main\")" + }, + description: { + label: "الوصف", + helpText: "وصف الصفحة للتنقل" + }, + source: { + label: "المصدر", + helpText: "النص المصدري للصفحة. للنوع 'html' يكون JSX مقيَّدًا يُترجَم إلى شجرة المكوّنات وقت الحفظ — يُحلَّل ولا يُنفَّذ أبدًا. وللنوع 'react' يكون React حقيقيًا يُنفّذه وقت التشغيل الموثوق عند العرض." + }, + object: { + label: "الكائن", + helpText: "الكائن المرتبط (لصفحات السجلات)" + }, + variables: { + label: "المتغيرات", + helpText: "متغيرات الحالة المحلية للصفحة" + }, + "variables.name": { + label: "الاسم", + helpText: "اسم المتغير — يُتاح للتعبيرات بالصيغة `page.`" + }, + "variables.type": { + label: "النوع", + helpText: "نوع القيمة" + }, + "variables.defaultValue": { + label: "القيمة الافتراضية", + helpText: "القيمة الأولية (افتراضيًا قيمة فارغة مناسبة للنوع)" + }, + "variables.source": { + label: "يكتبه", + helpText: "المكوّن (بمعرّفه) الذي يكتب هذا المتغير — مثل element:record_picker" + }, + regions: { + label: "المناطق", + helpText: "مناطق التخطيط (header، main، sidebar، footer) مع المكوّنات" + }, + "regions.name": { + label: "المنطقة" + }, + "regions.width": { + label: "العرض" + }, + "regions.components": { + label: "المكوّنات" + }, + interfaceConfig: { + label: "تهيئة الواجهة", + helpText: "الصفحة هي طريقة العرض: يختار source الكائن، وتُعرَّف columns/filterBy هنا مباشرة؛ ويحدد appearance.allowedVisualizations قائمة العارضات المسموح بها (إدخال واحد = مقفل)؛ ويبدّل userActions شريط الأدوات." + }, + "interfaceConfig.source": { + label: "المصدر", + helpText: "الكائن الذي تقرأ منه هذه الصفحة" + }, + "interfaceConfig.columns": { + label: "الأعمدة", + helpText: "الأعمدة المراد عرضها — تُعرَّف مباشرة على الصفحة (فارغ = كل حقول الكائن)" + }, + "interfaceConfig.filterBy": { + label: "التصفية حسب", + helpText: "عامل تصفية أساسي دائم للصفحة — المنشئ المرئي نفسه المستخدم في شريط أدوات القائمة." + }, + "interfaceConfig.sort": { + label: "الفرز", + helpText: "ترتيب الفرز الافتراضي للصفحة، يُعرَّف مباشرة على الصفحة." + }, + "interfaceConfig.sort.field": { + label: "الحقل" + }, + "interfaceConfig.sort.order": { + label: "الاتجاه" + }, + "interfaceConfig.levels": { + label: "المستويات", + helpText: "مستويات التسلسل الهرمي المراد عرضها (للمصادر الشجرية)" + }, + "interfaceConfig.appearance": { + label: "المظهر", + helpText: "التصورات المسموح بها (شبكة / كانبان / تقويم / …) وإظهار الوصف" + }, + "interfaceConfig.userFilters": { + label: "عوامل تصفية المستخدم", + helpText: "شريط التصفية للمستخدم النهائي: بلا (دون شريط) / علامات تبويب (إعدادات مسبقة مُسمّاة) / قائمة منسدلة (لكل حقل). «بلا» تزيل التهيئة." + }, + "interfaceConfig.userActions": { + label: "إجراءات المستخدم", + helpText: "مفاتيح شريط الأدوات (البحث، والفرز، والتصفية، وارتفاع الصف)" + }, + "interfaceConfig.addRecord": { + label: "إضافة سجل", + helpText: "نقطة الدخول لإضافة السجل" + }, + "interfaceConfig.addRecord.enabled": { + label: "مُفعَّل" + }, + "interfaceConfig.addRecord.position": { + label: "الموضع" + }, + "interfaceConfig.emptyState": { + label: "الحالة الفارغة", + helpText: "ما تعرضه الصفحة عندما لا تحتوي قائمتها على صفوف: عنوان، ورسالة، وأيقونة، ورابط واحد. عند تعيينها هنا، تحل محل الحالة الفارغة لطريقة العرض المصدرية." + }, + "interfaceConfig.buttons": { + label: "الأزرار", + helpText: "أزرار شريط الأدوات — اخترها من إجراءات هذا الكائن" + }, + "interfaceConfig.recordAction": { + label: "إجراء السجل", + helpText: "كيف يفتح النقر على سجل تفاصيله" + }, + "interfaceConfig.showRecordCount": { + label: "إظهار عدد السجلات", + helpText: "إظهار شريط عدد السجلات" + }, + "interfaceConfig.allowPrinting": { + label: "السماح بالطباعة", + helpText: "السماح للمستخدمين بطباعة هذه الصفحة" + }, + isDefault: { + label: "افتراضي", + helpText: "فتح صفحة السجل هذه لسجلات كائنها. صفحة سجل افتراضية واحدة لكل كائن: تُرفض الثانية." + }, + kind: { + label: "النوع", + helpText: "وضع تجاوز الصفحة: full أو slotted (لصفحات السجلات)" + }, + aria: { + label: "سمات ARIA", + helpText: "سمات إمكانية الوصول (تسميات ARIA، والأدوار)" + } + } + }, + dashboard: { + label: "لوحة المعلومات", + sections: { + basics: { + label: "الأساسيات", + description: "هوية لوحة المعلومات ووصفها." + }, + layout: { + label: "التخطيط", + description: "تحجيم الشبكة ووتيرة التحديث." + }, + widgets: { + label: "عناصر الواجهة", + description: "البطاقات والمخططات البيانية الموضوعة على الشبكة." + }, + filters: { + label: "عوامل التصفية", + description: "عوامل التصفية الافتراضية والعامة المطبَّقة على عناصر الواجهة." + }, + advanced: { + label: "متقدم", + description: "ضبط إمكانية الوصول والأداء." + } + }, + fields: { + name: { + label: "الاسم", + helpText: "معرّف فريد بصيغة snake_case" + }, + label: { + label: "التسمية", + helpText: "اسم العرض" + }, + description: { + label: "الوصف" + }, + columns: { + label: "الأعمدة", + helpText: "أعمدة الشبكة (الافتراضي 12)" + }, + gap: { + label: "التباعد", + helpText: "المسافة بين عناصر الواجهة، بخطوات من 0.25rem ‏(4 = 1rem)" + }, + refreshIntervalSeconds: { + label: "فاصل التحديث بالثواني", + helpText: "التحديث التلقائي (بالثواني)" + }, + header: { + label: "الترويسة", + helpText: "تهيئة ترويسة لوحة المعلومات (العنوان، والعنوان الفرعي، والإجراءات)" + }, + "header.showTitle": { + label: "إظهار العنوان", + helpText: "إظهار عنوان لوحة المعلومات في الترويسة" + }, + "header.showDescription": { + label: "إظهار الوصف", + helpText: "إظهار وصف لوحة المعلومات في الترويسة" + }, + "header.actions": { + label: "الإجراءات", + helpText: "أزرار إجراءات الترويسة" + }, + "header.actions.label": { + label: "التسمية" + }, + "header.actions.actionUrl": { + label: "رابط الإجراء" + }, + "header.actions.actionType": { + label: "نوع الإجراء" + }, + "header.actions.icon": { + label: "الأيقونة" + }, + widgets: { + label: "عناصر الواجهة", + helpText: "عناصر واجهة لوحة المعلومات مع الموضع والحجم" + }, + "widgets.id": { + label: "معرّف عنصر الواجهة" + }, + "widgets.title": { + label: "العنوان" + }, + "widgets.description": { + label: "الوصف" + }, + "widgets.type": { + label: "نوع التصور" + }, + "widgets.chartConfig": { + label: "تهيئة المخطط البياني" + }, + "widgets.colorVariant": { + label: "متغير اللون" + }, + "widgets.requiresObject": { + label: "يتطلب كائنًا" + }, + "widgets.requiresService": { + label: "يتطلب خدمة" + }, + "widgets.filter": { + label: "عامل التصفية" + }, + "widgets.compareTo": { + label: "المقارنة مع" + }, + "widgets.dataset": { + label: "مجموعة البيانات" + }, + "widgets.dimensions": { + label: "الأبعاد" + }, + "widgets.values": { + label: "القيم" + }, + "widgets.layout": { + label: "التخطيط" + }, + "widgets.options": { + label: "الخيارات" + }, + "widgets.filterBindings": { + label: "روابط عوامل التصفية" + }, + "widgets.suppressWarnings": { + label: "إخفاء التحذيرات" + }, + dateRange: { + label: "النطاق الزمني", + helpText: "محدد النطاق الزمني الافتراضي" + }, + globalFilters: { + label: "عوامل التصفية العامة", + helpText: "عوامل التصفية المطبَّقة على جميع عناصر الواجهة" + }, + "globalFilters.name": { + label: "الاسم" + }, + "globalFilters.field": { + label: "الحقل" + }, + "globalFilters.object": { + label: "الكائن" + }, + "globalFilters.label": { + label: "التسمية" + }, + "globalFilters.type": { + label: "نوع الإدخال" + }, + "globalFilters.options": { + label: "الخيارات" + }, + "globalFilters.optionsFrom": { + label: "الخيارات من" + }, + "globalFilters.defaultValue": { + label: "القيمة الافتراضية" + }, + "globalFilters.scope": { + label: "النطاق" + }, + "globalFilters.targetWidgets": { + label: "عناصر الواجهة المستهدفة" + } + } + }, + app: { + label: "التطبيق", + sections: { + basics: { + label: "الأساسيات", + description: "هوية التطبيق وتفعيله." + }, + navigation: { + label: "التنقل", + description: "عناصر الشريط الجانبي وتجميع المناطق." + }, + content: { + label: "المحتوى", + description: "ربط المساعد المحيط. تُعلَن الكائنات ونقاط النهاية على الحزمة، لا على التطبيق." + }, + branding: { + label: "العلامة التجارية", + description: "ألوان السمة والشعار." + }, + access_and_sharing: { + label: "الوصول والمشاركة", + description: "من يستطيع الوصول إلى هذا التطبيق. الوصول العام والتضمين يُضبطان لكل طريقة عرض نموذج، لا على مستوى التطبيق." + } + }, + fields: { + name: { + label: "الاسم", + helpText: "بصيغة snake_case، فريد" + }, + label: { + label: "التسمية" + }, + description: { + label: "الوصف" + }, + icon: { + label: "الأيقونة", + helpText: "اسم أيقونة Lucide ‏(مثل \"users\"، \"briefcase\")" + }, + active: { + label: "نشط" + }, + isDefault: { + label: "افتراضي", + helpText: "جعل هذا التطبيق الافتراضي للمستخدمين الجدد" + }, + hidden: { + label: "مخفي", + helpText: "إبقاء هذا التطبيق خارج مبدّل التطبيقات — تُظهره الواجهة من قائمة الصورة الرمزية بدلًا من ذلك. للتنقل فقط: يظل التطبيق المخفي قابلًا للتوجيه بالكامل وخاضعًا لفحص الأذونات." + }, + navigation: { + label: "التنقل", + helpText: "شجرة التنقل — بنية متداخلة" + }, + areas: { + label: "المناطق", + helpText: "تجميع العناصر في مناطق قابلة للطي" + }, + "areas.id": { + label: "المعرّف" + }, + "areas.label": { + label: "التسمية" + }, + "areas.icon": { + label: "الأيقونة" + }, + "areas.description": { + label: "الوصف" + }, + "areas.navigation": { + label: "التنقل" + }, + defaultAgent: { + label: "الوكيل الافتراضي", + helpText: "وكيل المنصة للمساعد المحيط ('ask' افتراضيًا؛ أما 'build' فلواجهات التأليف)" + }, + branding: { + label: "العلامة التجارية", + helpText: "الألوان الأساسية/الثانوية، والشعار، والسمة" + }, + requiredPermissions: { + label: "الأذونات المطلوبة", + helpText: "الأذونات اللازمة للوصول إلى هذا التطبيق" + } + } + }, + action: { + label: "الإجراء", + sections: { + basics: { + label: "الأساسيات", + description: "هوية الإجراء وطريقة عرضه." + }, + behavior: { + label: "السلوك", + description: "تهيئة ما يحدث عند تشغيل الإجراء." + }, + placement: { + label: "الموضع", + description: "أين ومتى يظهر زر الإجراء." + }, + advanced: { + label: "متقدم", + description: "إتاحة الإجراء للذكاء الاصطناعي وشكل طلب API." + } + }, + fields: { + name: { + label: "الاسم", + helpText: "معرّف فريد (snake_case)" + }, + label: { + label: "التسمية", + helpText: "نص الزر المعروض للمستخدمين" + }, + objectName: { + label: "اسم الكائن", + helpText: "الكائن الذي ينتمي إليه هذا الإجراء (اختياري)" + }, + icon: { + label: "الأيقونة", + helpText: "اسم أيقونة Lucide ‏(مثل \"check\"، \"x-circle\")" + }, + type: { + label: "النوع", + helpText: "ما يحدث عند النقر" + }, + variant: { + label: "النمط", + helpText: "نمط الزر (primary=أزرق، danger=أحمر، ghost=شفاف)" + }, + mode: { + label: "الوضع", + helpText: "الوضع الدلالي للإجراء. لا يقرؤه حاليًا إلا استدلال إشراك الإنسان في حلقة الذكاء الاصطناعي — ولا يتفرّع عليه أي عارض." + }, + order: { + label: "الترتيب", + helpText: "ترتيب الفرز ضمن مجموعة الموقع — الأصغر يأتي أولًا، وتتخذ ترويسة السجل الأول زرًّا أساسيًا لها. عند عدم التعيين يُحفظ ترتيب التسجيل." + }, + target: { + label: "الهدف", + helpText: "عنوان URL، أو اسم التدفق، أو نقطة نهاية API المراد استدعاؤها" + }, + method: { + label: "الأسلوب", + helpText: "أسلوب HTTP ‏(GET، POST، PUT، DELETE)" + }, + body: { + label: "المتن", + helpText: "إما تعبير L1 أو متن JS معزول من المستوى L2" + }, + "body.language": { + label: "اللغة", + helpText: "expression = صيغة خالصة؛ js = JavaScript معزول" + }, + "body.source": { + label: "المصدر", + helpText: "مصدر متن الدالة — بلا عمليات استيراد على المستوى الأعلى" + }, + "body.capabilities": { + label: "القدرات", + helpText: "واجهات ctx المسموح بها (api.read، api.write، crypto.uuid، log، …)" + }, + "body.timeoutMs": { + label: "المهلة (مللي ثانية)", + helpText: "المهلة لكل استدعاء (مللي ثانية)" + }, + "body.memoryMb": { + label: "الذاكرة (ميغابايت)", + helpText: "حد الذاكرة لكل استدعاء (ميغابايت، بحد أقصى 256)" + }, + params: { + label: "المعلمات", + helpText: "معلمات إدخال المستخدم (إظهار نموذج قبل التنفيذ)" + }, + "params.name": { + label: "الاسم" + }, + "params.field": { + label: "الحقل" + }, + "params.objectOverride": { + label: "تجاوز الكائن" + }, + "params.label": { + label: "التسمية" + }, + "params.type": { + label: "النوع" + }, + "params.widget": { + label: "عنصر الواجهة" + }, + "params.required": { + label: "مطلوب" + }, + "params.options": { + label: "الخيارات" + }, + "params.placeholder": { + label: "النص النائب" + }, + "params.helpText": { + label: "نص المساعدة" + }, + "params.defaultValue": { + label: "القيمة الافتراضية" + }, + "params.multiple": { + label: "متعدد" + }, + "params.accept": { + label: "الأنواع المقبولة" + }, + "params.maxSize": { + label: "الحد الأقصى للحجم (بايت)" + }, + "params.reference": { + label: "الكائن المُشار إليه" + }, + "params.defaultFromRow": { + label: "القيمة الافتراضية من الصف" + }, + "params.carryOver": { + label: "الترحيل" + }, + "params.visible": { + label: "مرئي عندما" + }, + "params.requiresFeature": { + label: "يتطلب الميزة" + }, + operation: { + label: "العملية", + helpText: "كتابة حقول تصريحية في سجل واحد: 'update' تطبّق `patch`، مدموجًا تحت `params` المُجمَّعة، على السجل الحالي بهوية المستدعي — دون رفع صلاحيات النظام أبدًا، فتسري أذونات المستدعي وخطافات الكائن وعمليات التحقق الخاصة به كما في تحرير يجريه المستخدم." + }, + patch: { + label: "الرقعة", + helpText: "قيم حقول ثابتة يكتبها التحديث في السجل الحالي، مثل {\"status\": \"done\"} — تُدمج تحت القيم التي تجمعها `params`، فتغلب المعلمة ذات الاسم نفسه. تُكتب بهوية المستدعي: تسري أذونات الكائن وخطافاته وعمليات التحقق كما في تحرير يجريه المستخدم." + }, + undoable: { + label: "قابل للتراجع", + helpText: "عرض خيار تراجع بعد نجاح هذا التحديث. يلتقط التراجع القيمة السابقة لكل حقل يكتبه الإجراء — المجموعة المدموجة، `patch` تحت `params` المُجمَّعة. الإجراء الذي لا يحمل `operation` لا يعلن مجموعة كتابة، فلا يوجد ما يُلتقط." + }, + execution: { + label: "التنفيذ", + helpText: "عقد الإرسال الجماعي الذي كُتب متن هذا الإجراء له: 'perRecord' يرسل عملية إرسال واحدة لكل صف محدد تحمل recordId الخاص بذلك الصف؛ أما 'aggregate' فيرسل عملية إرسال واحدة للتحديد كله، مع كل المعرّفات في params._selectedIds. عند الإغفال، يُرسَل الإجراء لكل سجل." + }, + description: { + label: "الوصف", + helpText: "سطر توضيحي تحت عنوان مربع حوار معلمات هذا الإجراء. في الإجراء الذي يجمع معلمات، يوضع سؤال التأكيد هنا لا في confirmText — مربع حوار واحد لا اثنان. ليس ai.description، فذلك هو النص الذي يقرؤه وكيل الذكاء الاصطناعي." + }, + submitText: { + label: "تسمية زر الإرسال", + helpText: "تسمية زر الإرسال في مربع حوار معلمات هذا الإجراء، أي الفعل الذي ينفّذه الزر (مثل \"إرسال\"، \"تسجيل مكالمة\"). للإجراء الذي يجمع معلمات فقط. اتركه فارغًا لإظهار \"تأكيد\"." + }, + cancelText: { + label: "تسمية زر الإلغاء", + helpText: "تسمية زر الإغلاق في مربع الحوار الذي يفتحه هذا الإجراء قبل تشغيله، سواء أكان مربع التأكيد أم مربع المعلمات. سمِّ ما يُبقي عليه الإغلاق حين يكون فعل الإجراء نفسه \"إلغاء …\" (مثل \"الإبقاء على الدعوة\"). للإجراء الذي يطلب تأكيدًا أو يجمع معلمات فقط. اتركه فارغًا لإظهار \"إلغاء\"." + }, + confirmText: { + label: "نص التأكيد", + helpText: "رسالة التأكيد (مثل \"هل أنت متأكد؟\")" + }, + successMessage: { + label: "رسالة النجاح", + helpText: "رسالة النجاح بعد الاكتمال" + }, + errorMessage: { + label: "رسالة الخطأ", + helpText: "رسالة الخطأ المعروضة عند فشل الإجراء، بدلًا من الخطأ الخام." + }, + outcomeMessages: { + label: "رسائل النتائج", + helpText: "رسالة نجاح لكل نتيجة من نتائج المعالج، بصيغة JSON: كل مفتاح قيمة `outcome` بصيغة snake_case يعيدها المعالج في حمولة نجاحه، وكل قيمة هي التسمية المعروضة لتلك النتيجة، مثل {\"archived\": \"مؤرشف\", \"already_archived\": \"مؤرشف مسبقًا\"}. يجوز أن تحتوي التسمية على فراغات {{ result.* }}. النتيجة التي لا إدخال لها تُظهر رسالة النجاح؛ والمفتاح الذي لا يعيده المعالج أبدًا لا يظهر أبدًا. غير مسموح بجانب مربع حوار النتيجة أو `operation: 'update'`." + }, + onSuccess: { + label: "عند النجاح", + helpText: "التنقل بعد نجاح الإجراء: المسار المراد فتحه، وأين يُفتح." + }, + "onSuccess.navigate": { + label: "التنقل", + helpText: "قالب مسار أو عنوان URL يُفتح بعد نجاح الإجراء. يجوز أن يستبدل ${param.*} ‏(القيم التي جمعها مربع حوار المعلمات)، و${ctx.*} ‏(origin، apiBase، user، org، recordId، selection)، و${result.*} ‏(استجابة الخادم، مثل ${result.id}). القيمة النسبية مسار داخل التطبيق." + }, + "onSuccess.openIn": { + label: "الفتح في", + helpText: "أين يُفتح التنقل: 'self' (الافتراضي) ينتقل في المكان نفسه؛ أما 'newTab' فيفتح علامة تبويب جديدة في المتصفح." + }, + refreshAfter: { + label: "التحديث بعد", + helpText: "تحديث القائمة/الصفحة بعد اكتمال الإجراء" + }, + openIn: { + label: "الفتح في", + helpText: "أين يُفتح عنوان URL الثابت في `target` — 'self' ينتقل في المكان نفسه، أما 'new-tab' فيفتح علامة تبويب جديدة في المتصفح. عند الإغفال، يُفتح عنوان URL المطلق أو الخارجي في علامة تبويب جديدة، وينتقل النسبي في المكان نفسه." + }, + opensInNewTab: { + label: "يُفتح في علامة تبويب جديدة", + helpText: "فتح نتيجة الإجراء في علامة تبويب جديدة: يفتح العارض علامة التبويب مسبقًا بشكل متزامن عند النقر (آمن من مانع النوافذ المنبثقة) وينقلها إلى redirectUrl الخاص بالمعالج. يختلف عن `openIn`، الذي يوجّه هدف URL ثابتًا." + }, + newTabUrl: { + label: "مسار علامة التبويب الجديدة", + helpText: "مسار على هذه الواجهة الخلفية، يبدأ بـ / (مثل /sso-open/{recordId})، مع عنصر نائب {recordId}. عند تعيينه مع `opensInNewTab` ينقل العارض علامة التبويب المفتوحة مسبقًا فورًا إلى أصل الواجهة الخلفية + هذا المسار ولا يرسل شيئًا — لذا يجب أن تفرض نقطة النهاية المصادقة بنفسها. يُرفض عنوان URL المطلق؛ وللصفحة الخارجية استخدم إجراء url مع ضبط «الفتح في» على new-tab." + }, + locations: { + label: "المواقع", + helpText: "أين يظهر هذا الإجراء (شريط الأدوات، قائمة الصف، إلخ)" + }, + component: { + label: "المكوّن", + helpText: "طريقة العرض (زر، أيقونة، عنصر قائمة)" + }, + visible: { + label: "مرئي", + helpText: "تعبير CEL: الإظهار فقط عندما يكون الشرط صحيحًا" + }, + disabled: { + label: "معطَّل", + helpText: "تعبير CEL: التعطيل عندما يكون الشرط صحيحًا" + }, + disabledReason: { + label: "سبب التعطيل", + helpText: "سبب ظهور الإجراء باهتًا، يُعرض عند التمرير وتقرؤه قارئات الشاشة ما دام «معطّل» صحيحًا (مثل \"يلزم أولًا وجود مراجعة قانونية معتمدة.\"). للعرض فقط: يظل الخادم يرفض الاستدعاء وفق قواعده. اتركه فارغًا لإظهار \"غير متاح لهذا السجل\"." + }, + requiresFeature: { + label: "يتطلب الميزة", + helpText: "علامة ميزة مصادقة عامة تحكم هذا الإجراء. تُحوَّل إلى المسند `visible` وقت التحليل وتُزال من المخرجات، فلا يرى أي مستهلك لاحق المفتاح أبدًا." + }, + requiresMembershipReach: { + label: "يتطلب مدى العضوية", + helpText: "نقطة نهاية المؤسسة التي يستدعيها هذا الإجراء، فلا يُعرض إلا على الأعضاء الذين تبلغ درجتهم تلك النقطة. يُحوَّل إلى المسند `visible` وقت التحليل ويُزال من المخرجات، فلا يرى أي مستهلك لاحق المفتاح أبدًا." + }, + requiredPermissions: { + label: "الأذونات المطلوبة", + helpText: "القدرات (systemPermissions في مجموعة الأذونات) التي يجب أن يحملها المستدعي — كل واحدة مُدرجة — لاستدعاء هذا الإجراء (ADR-0066 D4). يرفض باب إجراءات المنصة أي شخص آخر بالرمز 403 في كل استدعاء يرسله، بما في ذلك مسار MCP/الذكاء الاصطناعي، ويُخفى الزر عنهم. تبقى نقطة نهاية إجراء من نوع api قابلة للوصول مباشرة، لذا يجب أن تتحقق من هذه القدرات بنفسها؛ ولإجراء api بهدف خارج الأصل لا تتحقق المنصة من شيء من جهة الخادم." + }, + ai: { + label: "الذكاء الاصطناعي", + helpText: "الإتاحة للذكاء الاصطناعي (اختيارية): عيّن ai.exposed=true واكتب ai.description ‏(40 حرفًا على الأقل) لجعله قابلًا للاستدعاء من الوكلاء." + }, + recordIdParam: { + label: "معلمة معرّف السجل", + helpText: "اسم معلمة المتن لمعرّف السجل" + }, + recordIdField: { + label: "حقل معرّف السجل", + helpText: "الحقل المستخدم كمعرّف للسجل (الافتراضي: \"id\")" + }, + bodyShape: { + label: "شكل المتن", + helpText: "بنية متن الطلب (مسطّحة أو متداخلة)" + }, + bodyExtra: { + label: "إضافات المتن", + helpText: "حقول ثابتة في متن الطلب لإجراء api هذا، تُدمج أخيرًا فتتجاوز المعلمات المُجمَّعة (مثل {\"resend\": true}). يحلّ وقت التشغيل رموز متغيرات الصفحة (page.NAME بين أقواس مزدوجة). تُوضع الحمولة هنا، لا في params أبدًا." + } + } + }, + report: { + label: "التقرير", + sections: { + basics: { + label: "الأساسيات", + description: "الهوية ونوع التقرير." + }, + dataset_binding: { + label: "ربط مجموعة البيانات", + description: "مجموعة البيانات في الطبقة الدلالية التي يعرضها هذا التقرير. القيم هي مقاييس مجموعة البيانات؛ والصفوف هي أبعادها." + }, + joined_blocks: { + label: "الكتل المدمجة", + description: "كتل إضافية مرتبطة بمجموعات بيانات تُكدَّس في تقرير واحد (للتقارير المدمجة فقط)." + }, + filter_and_chart: { + label: "التصفية والمخطط البياني", + description: "عامل تصفية النطاق وقت العرض وطريقة عرض المخطط البياني." + }, + period_and_export: { + label: "الفترة والتصدير", + description: "الفترة الافتراضية للتقرير والصيغ التي يمكن تنزيل جدوله بها." + } + }, + fields: { + name: { + label: "الاسم", + helpText: "معرّف فريد بصيغة snake_case" + }, + label: { + label: "التسمية" + }, + description: { + label: "الوصف" + }, + type: { + label: "النوع", + helpText: "نوع التقرير: tabular/summary/matrix/joined" + }, + dataset: { + label: "مجموعة البيانات", + helpText: "مجموعة البيانات المراد ربطها (تأتي المقاييس/الأبعاد من طبقتها الدلالية)" + }, + values: { + label: "القيم", + helpText: "أسماء المقاييس (من مجموعة البيانات) المراد عرضها" + }, + rows: { + label: "الصفوف", + helpText: "أسماء الأبعاد (من مجموعة البيانات) المراد تجميع الصفوف حسبها" + }, + columns: { + label: "الأعمدة", + helpText: "أسماء الأبعاد أفقيًا (للمصفوفة فقط)" + }, + order: { + label: "الترتيب", + helpText: "مفاتيح الفرز، الأكثر أهمية أولًا (بُعد من الصفوف/الأعمدة أو مقياس من القيم). الأبعاد الزمنية مرتبة زمنيًا افتراضيًا." + }, + "order.by": { + label: "الترتيب حسب" + }, + "order.direction": { + label: "الاتجاه" + }, + drilldown: { + label: "التعمق", + helpText: "انقر على صف/خلية مجمَّعة لفتح السجلات الكامنة" + }, + blocks: { + label: "الكتل", + helpText: "تقارير فرعية مرتبطة بمجموعات بيانات (للتقرير المدمج فقط)" + }, + "blocks.name": { + label: "الاسم" + }, + "blocks.label": { + label: "التسمية" + }, + "blocks.description": { + label: "الوصف" + }, + "blocks.type": { + label: "نوع الكتلة" + }, + "blocks.dataset": { + label: "مجموعة البيانات" + }, + "blocks.rows": { + label: "الصفوف" + }, + "blocks.columns": { + label: "الأعمدة" + }, + "blocks.values": { + label: "القيم" + }, + "blocks.runtimeFilter": { + label: "عامل تصفية وقت التشغيل" + }, + "blocks.order": { + label: "الترتيب" + }, + runtimeFilter: { + label: "عامل تصفية وقت التشغيل", + helpText: "عامل تصفية النطاق وقت العرض، يُركَّب بـ AND وقت الاستعلام" + }, + chart: { + label: "المخطط البياني", + helpText: "تهيئة المخطط البياني (النوع، ووسيلة الإيضاح، والألوان)" + }, + dateRange: { + label: "النطاق الزمني", + helpText: "الفترة الافتراضية ومفتاح النطاق المخصّص. يجب أن يكون البُعد بُعدًا تاريخيًا من مجموعة البيانات؛ اتركه فارغًا حين لا تحتوي مجموعة البيانات إلا على بُعد واحد منه." + }, + exportOptions: { + label: "خيارات التصدير", + helpText: "صيغ التنزيل (csv، xlsx) واسم الملف. من يحق له التنزيل يحدده إذن التصدير على كائن مجموعة البيانات." + } + } + }, + dataset: { + label: "مجموعة البيانات", + description: "الطبقة الدلالية للتحليلات — الأبعاد والمقاييس", + sections: { + basics: { + label: "الأساسيات", + description: "هوية مجموعة البيانات." + }, + source: { + label: "المصدر", + description: "الكائن الأساسي، والعلاقات المراد ضمّها، والنطاق الذاتي لمجموعة البيانات. تُشتق عمليات الضم من رسم الكائنات البياني — اختر أسماء العلاقات (lookup / master_detail)، ولا تكتب عبارة ON أبدًا." + }, + dimensions: { + label: "الأبعاد", + description: "محاور قابلة للتجميع. استخدم حقلًا أساسيًا، أو `relationship.field` (مثل account.region) لعلاقة مضمَّنة أعلاه." + }, + measures: { + label: "المقاييس", + description: "قيم قابلة للتجميع تُعرَّف مرة واحدة ويُشار إليها بالاسم. المقياس هو sum/avg/count/… لحقل ما؛ والمقياس المشتق يجمع مقاييس أخرى (ratio/sum/difference/product). تُحرَّر عوامل التصفية الخاصة بالمقياس والعمليات المشتقة لكل صف في مصمم مجموعات البيانات." + } + }, + fields: { + name: { + label: "الاسم", + helpText: "معرّف فريد بصيغة snake_case" + }, + label: { + label: "التسمية", + helpText: "اسم العرض" + }, + description: { + label: "الوصف", + helpText: "ما تقيسه مجموعة البيانات هذه" + }, + object: { + label: "الكائن", + helpText: "الكائن الأساسي — عبارة FROM" + }, + include: { + label: "التضمين", + helpText: "أسماء حقول العلاقات (lookup / master_detail) المراد ضمّها — تتيح أبعادًا/مقاييس بالصيغة `relationship.field` ‏(مثل تضمين \"account\" ← التجميع حسب account.region)" + }, + filter: { + label: "عامل التصفية", + helpText: "عامل تصفية النطاق الذاتي (مثل استبعاد السجلات المحذوفة حذفًا ناعمًا)، يُركَّب بـ AND في كل استعلام" + }, + dimensions: { + label: "الأبعاد", + helpText: "لكل منها: الاسم (تشير إليه طرق العرض)، والحقل، والنوع، و— للتواريخ — دقة تجميع افتراضية" + }, + "dimensions.name": { + label: "الاسم" + }, + "dimensions.label": { + label: "التسمية" + }, + "dimensions.field": { + label: "الحقل" + }, + "dimensions.type": { + label: "النوع" + }, + "dimensions.dateGranularity": { + label: "دقة التاريخ" + }, + "dimensions.nullLabel": { + label: "تسمية القيمة الفارغة" + }, + measures: { + label: "المقاييس", + helpText: "لكل منها: الاسم، والتجميع، والحقل (اختياري في count)، وتنسيق العرض/العملة" + }, + "measures.name": { + label: "الاسم" + }, + "measures.label": { + label: "التسمية" + }, + "measures.aggregate": { + label: "التجميع" + }, + "measures.field": { + label: "الحقل" + }, + "measures.filter": { + label: "عامل التصفية" + }, + "measures.format": { + label: "التنسيق" + }, + "measures.currency": { + label: "العملة" + }, + "measures.polarity": { + label: "القطبية" + }, + "measures.derived": { + label: "مشتق من" + }, + "measures.duration": { + label: "المدة" + } + } + }, + flow: { + label: "التدفق", + sections: { + basics: { + label: "الأساسيات", + description: "هوية التدفق وكيف يبدأ." + }, + canvas: { + label: "لوحة الرسم", + description: "العُقد والحواف ومتغيرات التدفق — فكّر في استخدام المصمم المرئي للتدفقات المعقدة." + }, + execution: { + label: "التنفيذ", + description: "حالة النشر، والهوية، ومعالجة الأخطاء." + } + }, + fields: { + name: { + label: "الاسم", + helpText: "معرّف فريد (snake_case)" + }, + label: { + label: "التسمية", + helpText: "اسم العرض للمستخدمين" + }, + type: { + label: "النوع", + helpText: "كيف يبدأ التدفق (autolaunched، record_change، schedule، screen، api)" + }, + description: { + label: "الوصف", + helpText: "ما يفعله هذا التدفق" + }, + nodes: { + label: "العُقد", + helpText: "⚠️ فكّر في استخدام المحرر المرئي «مصمم التدفقات» بدلًا من JSON" + }, + "nodes.id": { + label: "المعرّف" + }, + "nodes.type": { + label: "نوع العقدة" + }, + "nodes.label": { + label: "التسمية" + }, + "nodes.config": { + label: "التهيئة" + }, + "nodes.connectorConfig": { + label: "إجراء الموصل" + }, + "nodes.position": { + label: "الموضع على لوحة الرسم" + }, + "nodes.timeoutMs": { + label: "المهلة (مللي ثانية)" + }, + "nodes.inputSchema": { + label: "مخطط الإدخال" + }, + "nodes.waitEventConfig": { + label: "حدث الانتظار" + }, + "nodes.boundaryConfig": { + label: "الحدث الحدّي" + }, + edges: { + label: "الحواف", + helpText: "الاتصالات بين العُقد — استخدم «مصمم التدفقات» لتحرير أسهل" + }, + "edges.id": { + label: "المعرّف" + }, + "edges.source": { + label: "من العقدة" + }, + "edges.target": { + label: "إلى العقدة" + }, + "edges.condition": { + label: "الشرط" + }, + "edges.type": { + label: "نوع الاتصال" + }, + "edges.label": { + label: "التسمية" + }, + "edges.isDefault": { + label: "المسار الافتراضي" + }, + variables: { + label: "المتغيرات", + helpText: "متغيرات التدفق (المدخلات/المخرجات)" + }, + "variables.name": { + label: "الاسم" + }, + "variables.type": { + label: "النوع" + }, + "variables.isInput": { + label: "إدخال" + }, + "variables.isOutput": { + label: "إخراج" + }, + "variables.defaultValue": { + label: "القيمة الافتراضية" + }, + "variables.objectName": { + label: "كائن السجل" + }, + status: { + label: "الحالة", + helpText: "حالة النشر: draft ← active ← obsolete" + }, + version: { + label: "الإصدار", + helpText: "رقم الإصدار (يُزاد تلقائيًا)" + }, + runAs: { + label: "التشغيل بهوية", + helpText: "التنفيذ بهوية system (المسؤول) أو user (أذونات المستخدم الحالي)" + }, + requiredPermissions: { + label: "الأذونات المطلوبة", + helpText: "القدرات (systemPermissions في مجموعة الأذونات) التي يجب أن يحملها المستدعي — كل واحدة مُدرجة — لبدء هذا التدفق. كل باب بدء (مسار المشغّل، وإجراء تدفق عبر REST أو عبر MCP، ونقطة نهاية تدفق) يرفض أي شخص آخر بالرمز 403، والإجراء الذي يبدأ هذا التدفق يرث القائمة. لا تُفحص خطوة التدفق الفرعي في التدفق الأصل مرة أخرى." + }, + errorHandling: { + label: "معالجة الأخطاء", + helpText: "ما يجب فعله عند فشل عقدة (fail، retry، continue)" + }, + successMessage: { + label: "رسالة النجاح", + helpText: "رسالة تُحمَل على نتيجة التشغيل عند اكتمال التدفق؛ وتعرضها واجهة تدفق الشاشة كإشعار منبثق بدلًا من \"تم\" العامة." + }, + errorMessage: { + label: "رسالة الخطأ", + helpText: "رسالة تُحمَل على نتيجة التشغيل عند فشل التدفق؛ وتعرضها واجهة تدفق الشاشة كإشعار منبثق بدلًا من الخطأ الخام." + } + } + }, + job: { + label: "مهمة خلفية" + }, + datasource: { + label: "مصدر البيانات" + }, + external_catalog: { + label: "الكتالوج الخارجي" + }, + api: { + label: "نقطة نهاية API", + description: "عنوان HTTP ثابت تستدعيه الأنظمة الأخرى لتشغيل تدفق أو للعمل مع السجلات في التطبيق" + }, + translation: { + label: "الترجمة" + }, + email_template: { + label: "قالب البريد الإلكتروني", + sections: { + identity: { + label: "الهوية", + description: "يخاطب المُرسِلون هذا القالب باسمه؛ وتحدد اللغة أي نسخة لغوية منه تُرسَل." + }, + subject: { + label: "الموضوع", + description: "سطر الموضوع. يدعم الاستبدال بالصيغة {{var.path}}." + }, + html_body: { + label: "متن HTML", + description: "متن HTML منسَّق. تحذف معظم برامج البريد ، لذا استخدم الأنماط المضمَّنة." + }, + plain_text_body: { + label: "متن النص العادي", + description: "بديل اختياري بنص عادي. عند إغفاله، تحذف الخدمة الوسوم من متن HTML لاشتقاقه. توفيره يحسّن تقييم البريد العشوائي." + }, + variables: { + label: "المتغيرات", + description: "المتغيرات المُعلنة. تُعرض كتلميحات في Studio ويتحقق منها sendTemplate() عندما تكون مطلوبة." + }, + delivery_overrides: { + label: "تجاوزات التسليم", + description: "تجاوزات اختيارية لكل قالب للحقلين From / Reply-To." + }, + status: { + label: "الحالة" + } + }, + fields: { + name: { + label: "الاسم", + helpText: "snake_case مفصول بنقاط (مثل auth.password_reset، crm.welcome)" + }, + label: { + label: "التسمية" + }, + locale: { + label: "اللغة", + helpText: "وسم BCP-47 — مثل en-US، zh-CN" + }, + description: { + label: "الوصف" + }, + subject: { + label: "الموضوع" + }, + bodyHtml: { + label: "متن HTML" + }, + bodyText: { + label: "متن النص" + }, + variables: { + label: "المتغيرات", + helpText: "[{ \"name\": \"user.name\", \"type\": \"string\", \"required\": true, \"description\": \"...\" }]" + }, + fromOverride: { + label: "تجاوز المُرسِل", + helpText: "{ \"name\": \"Acme Sales\", \"address\": \"sales@acme.com\" }" + }, + replyTo: { + label: "الرد إلى", + helpText: "عنوان البريد الإلكتروني للرد (Reply-To)" + }, + active: { + label: "نشط", + helpText: "عند إلغاء التحديد، تعيد sendTemplate() القيمة TEMPLATE_INACTIVE." + } + } + }, + doc: { + label: "الوثائق", + description: "صفحة مساعدة مكتوبة بصيغة Markdown تشحنها حزمة أو تكتبها بنفسك" + }, + book: { + label: "كتاب الوثائق", + description: "جدول محتويات يجمّع صفحات المساعدة في أقسام مرتبة" + }, + permission: { + label: "مجموعة الأذونات", + sections: { + identity: { + label: "الهوية", + description: "مجموعات الأذونات هي الحاوية الوحيدة للقدرات: يحصل المستخدم على اتحاد كل المجموعات التي يحملها، فلا تضيف المجموعات إلا وصولًا. وتوزّع المناصب المجموعات على الأشخاص." + }, + system_permissions: { + label: "أذونات النظام", + description: "قدرات عالية المستوى غير مرتبطة بكائن محدد — مثل manage_users، view_audit_logs." + }, + object_and_field_permissions: { + label: "أذونات الكائنات والحقول", + description: "CRUD لكل كائن + FLS لكل حقل. حرّرها عبر محرر المصفوفة أو الصق JSON هنا." + }, + tab_and_row_level_security: { + label: "أمان علامات التبويب ومستوى الصف", + description: "ظهور علامات التبويب وسياسات RLS." + } + }, + fields: { + label: { + label: "التسمية", + helpText: "تسمية العرض للمسؤولين" + }, + name: { + label: "الاسم", + helpText: "تُملأ من الأحرف اللاتينية والأرقام في التسمية عند إنشاء المجموعة؛ وإن لم تحتوِ التسمية على أي منها، فاكتبها بنفسك. snake_case" + }, + description: { + label: "الوصف", + helpText: "وصف مقروء للبشر يُعرض في الإعداد." + }, + isDefault: { + label: "افتراضي", + helpText: "يمنح هذه المجموعة لكل من يسجّل الدخول، عبر المنصب المدمج everyone. المجموعة التي يعلنها التطبيق نفسه تُمنح عند بدء التشغيل؛ والمجموعة الآتية من حزمة مثبّتة تصبح اقتراحًا يؤكده المسؤول في الإعداد. لا تُمنح بهذه الطريقة أبدًا مجموعة تحمل أذونات عالية الامتياز. الافتراضي false." + }, + packageId: { + label: "معرّف الحزمة", + helpText: "الحزمة التي تشحن هذه المجموعة؛ وتُرفض أي قيمة تسمّي حزمة أخرى. اتركها فارغة لمجموعة مؤلَّفة في هذا النظام." + }, + systemPermissions: { + label: "أذونات النظام", + helpText: "قائمة مفاتيح قدرات النظام" + }, + adminScope: { + label: "نطاق الإدارة", + helpText: "الإدارة المفوَّضة: تتيح لحاملي هذه المجموعة إدارة شجرة فرعية واحدة من وحدات الأعمال. businessUnit هو جذر الشجرة الفرعية (sys_business_unit.name) وهو مطلوب وغير فارغ؛ أما includeSubtree فافتراضيه true؛ وأما manageAssignments فافتراضيه false ويتيح للحامل إسناد الأشخاص إلى المناصب، ومنح المجموعات مباشرة، داخل تلك الشجرة الفرعية؛ ولا يجوز للمفوَّض أن يمنح إلا المجموعات المذكورة في assignablePermissionSets. اتركه فارغًا لمجموعة لا تفوّض شيئًا." + }, + objects: { + label: "الكائنات", + helpText: "{ \"account\": { allowRead: true, allowEdit: true, ... } }" + }, + fields: { + label: "الحقول", + helpText: "{ \"account.amount\": { readable: true, editable: false } }" + }, + tabPermissions: { + label: "أذونات علامات التبويب", + helpText: "{ \"app_crm\": \"visible\", \"app_admin\": \"hidden\" }" + }, + rowLevelSecurity: { + label: "الأمان على مستوى الصف", + helpText: "مصفوفة من سياسات RLS ‏(راجع rls.zod.ts)" + } + } + }, + position: { + label: "المنصب", + sections: { + position: { + label: "المنصب", + description: "المنصب حزمة مسطّحة قابلة للإسناد من مجموعات الأذونات (مثل sales_rep، sales_manager): يسمّي المجموعات التي يوزّعها. تقع القدرات في مجموعات الأذونات؛ ويقع عمق الرؤية في شجرة وحدات الأعمال." + } + }, + fields: { + name: { + label: "الاسم", + helpText: "snake_case" + }, + label: { + label: "التسمية" + }, + description: { + label: "الوصف" + }, + permissionSets: { + label: "مجموعات الأذونات", + helpText: "مجموعات الأذونات التي يوزّعها هذا المنصب، بالاسم (snake_case)." + }, + delegatable: { + label: "قابل للتفويض", + helpText: "يجوز لحامليه تفويض هذا المنصب بأنفسهم، لمدة محددة ومع ذكر سبب. الافتراضي false — التفويض للمسؤولين فقط." + } + } + }, + capability: { + label: "القدرة", + description: "إذن تعرّفه حزمة؛ وتمنحه عبر مجموعات الأذونات" + }, + sharing_rule: { + label: "قاعدة المشاركة", + description: "قاعدة تعلنها حزمة لمشاركة السجلات؛ وتؤلّف المؤسسات قواعدها الخاصة في الإعداد" + }, + queue: { + label: "قائمة الانتظار", + description: "مجموعة مُسمّاة من الأشخاص يمكن أن تبقى السجلات فيها حتى يتولّى أحدهم السجل؛ وتزوّد المؤسسات قوائمها بالأشخاص في الإعداد" + }, + assignment_rule: { + label: "قاعدة الإسناد", + description: "قاعدة تعلنها حزمة لتوجيه السجلات الجديدة إلى قائمة انتظار أو إلى شخص" + }, + agent: { + label: "وكيل الذكاء الاصطناعي", + sections: { + identity: { + label: "الهوية", + description: "كيف يرى المستخدمون هذا الوكيل ويشيرون إليه." + }, + ai_configuration: { + label: "تهيئة الذكاء الاصطناعي", + description: "اختيار النموذج، والتعليمات، والتخطيط، والذاكرة." + }, + capabilities: { + label: "القدرات", + description: "المهارات ومصادر المعرفة التي يمكن للوكيل استخدامها." + }, + access: { + label: "الوصول والأمان", + description: "من يستطيع استخدام هذا الوكيل وما الضمانات التي تنطبق." + } + }, + fields: { + name: { + label: "الاسم", + helpText: "معرّف فريد (snake_case)" + }, + label: { + label: "التسمية", + helpText: "اسم العرض (مثل \"مساعد المبيعات\")" + }, + role: { + label: "الدور", + helpText: "شخصية الوكيل (مثل \"أخصائي دعم العملاء\")" + }, + avatar: { + label: "الصورة الرمزية", + helpText: "عنوان URL لصورة الصورة الرمزية" + }, + active: { + label: "نشط", + helpText: "تفعيل/تعطيل هذا الوكيل" + }, + surface: { + label: "الواجهة", + helpText: "واجهة المنتج التي يرتبط بها هذا الوكيل (ADR-0063 §1). لا تُرفق إلا المهارات التي تطابق واجهتها — أو تكون 'both' — ومجموعة أدوات الوكيل هي اتحاد أدوات تلك المهارات. الافتراضي 'ask'." + }, + instructions: { + label: "التعليمات", + helpText: "موجّه النظام — أخبر الوكيل كيف يتصرف وما الذي يستطيع فعله" + }, + model: { + label: "النموذج", + helpText: "تهيئة نموذج الذكاء الاصطناعي (المزوّد، واسم النموذج، ودرجة الحرارة، إلخ)" + }, + planning: { + label: "التخطيط", + helpText: "تهيئة الاستدلال الذاتي: الحد الأقصى لعدد دورات الاستدلال قبل أن يتوقف الوكيل (1–100، الافتراضي 10)." + }, + memory: { + label: "الذاكرة", + helpText: "الذاكرة طويلة المدى: ملاحظات مقطّرة تُحفظ لكل مستخدم، تُستدعى قبل كل محادثة ويكتبها تأمّل كل reflectionInterval من التفاعلات المُسلَّمة. عند تفعيل الذاكرة طويلة المدى، يكون الحقلان maxEntries، reflectionInterval مطلوبين. يفرضها وقت تشغيل الذكاء الاصطناعي السحابي." + }, + structuredOutput: { + label: "المخرجات المنظَّمة", + helpText: "عقد المخرجات للإجابة النهائية للوكيل: صيغة JSON، ومخطط JSON Schema الذي يُتحقق منها مقابله، وإعادة المحاولات، والصيغة الاحتياطية، وخطوات التحويل. يفرضه وقت تشغيل الذكاء الاصطناعي السحابي." + }, + skills: { + label: "المهارات", + helpText: "أسماء المهارات (بنية Agent←Skill←Tool)" + }, + access: { + label: "الوصول", + helpText: "معرّفات المستخدمين أو أسماء الأدوار التي يمكنها الدردشة مع هذا الوكيل" + }, + permissions: { + label: "الأذونات", + helpText: "الأذونات المطلوبة لاستخدام هذا الوكيل" + }, + guardrails: { + label: "حواجز الحماية", + helpText: "قواعد السلامة وسياسات المحتوى" + } + } + }, + tool: { + label: "أداة الذكاء الاصطناعي", + sections: { + basics: { + label: "الأساسيات", + description: "هوية الأداة ووصفها الموجّه للذكاء الاصطناعي." + }, + schemas: { + label: "المخططات", + description: "المدخلات التي تقبلها الأداة وشكل مخرجاتها." + } + }, + fields: { + name: { + label: "الاسم", + helpText: "معرّف فريد (snake_case)" + }, + label: { + label: "التسمية", + helpText: "اسم العرض لواجهة Studio" + }, + description: { + label: "الوصف", + helpText: "أخبر الذكاء الاصطناعي متى يستخدم هذه الأداة — كن محددًا!" + }, + objectName: { + label: "اسم الكائن", + helpText: "الكائن المرتبط (إذا كانت هذه الأداة تعمل على كائن محدد)" + }, + parameters: { + label: "المعلمات", + helpText: "معلمات الإدخال — عرّف خصائص مثل: {name: {type: \"string\", description: \"...\"}}" + }, + outputSchema: { + label: "مخطط المخرجات", + helpText: "مخطط المخرجات للتحقق (اختياري)" + } + } + }, + skill: { + label: "مهارة الذكاء الاصطناعي", + sections: { + basics: { + label: "الأساسيات", + description: "هوية المهارة ووصفها المقروء للبشر." + }, + ai_instructions: { + label: "تعليمات الذكاء الاصطناعي", + description: "كيف ينبغي للوكيل أن يستدل بهذه المهارة." + }, + triggers: { + label: "المشغّلات", + description: "متى ينبغي أن تُفعَّل هذه المهارة." + } + }, + fields: { + name: { + label: "الاسم", + helpText: "معرّف فريد (snake_case)" + }, + label: { + label: "التسمية", + helpText: "اسم العرض (مثل \"إدارة الحالات\")" + }, + description: { + label: "الوصف", + helpText: "ما تفعله هذه المهارة" + }, + active: { + label: "نشط", + helpText: "تفعيل/تعطيل هذه المهارة" + }, + surface: { + label: "الواجهة", + helpText: "واجهة الوكيل التي ترتبط بها هذه المهارة (ADR-0063 §3). ربطها بوكيل تختلف واجهته عنها فشلٌ صريح وقت الحل، لا تخطٍّ صامت. الافتراضي 'ask'." + }, + instructions: { + label: "التعليمات", + helpText: "تعليمات للذكاء الاصطناعي — أخبره كيف يستخدم هذه الأدوات معًا" + }, + tools: { + label: "الأدوات", + helpText: "أسماء الأدوات (تدعم حرف البدل: action_*)" + }, + triggerConditions: { + label: "شروط التشغيل", + helpText: "شروط برمجية (مثل objectName == \"case\")" + }, + "triggerConditions.field": { + label: "حقل السياق" + }, + "triggerConditions.operator": { + label: "العامل" + }, + "triggerConditions.value": { + label: "القيمة" + } + } + } +}; diff --git a/packages/platform-objects/src/apps/translations/ar.objects.generated.ts b/packages/platform-objects/src/apps/translations/ar.objects.generated.ts new file mode 100644 index 00000000000..0f73bdc3f1f --- /dev/null +++ b/packages/platform-objects/src/apps/translations/ar.objects.generated.ts @@ -0,0 +1,3857 @@ +// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. + +/** + * Auto-generated by 'os i18n extract' for locale 'ar'. + * Edit translations in place; re-run extract (with --merge) to fill new gaps. + * Do not hand-edit the structure — only the leaf string values. + * Merge only fills gaps: correcting a source label/description does not + * push the correction into a leaf here that already holds a translation — + * a present-but-stale string is not a gap, so it is left as-is. Re-translate + * it by hand when its source changes; nothing here or in `os i18n check` + * tells a leaf a translator updated on purpose from one nobody has looked + * at since the source moved. + */ + +import type { TranslationData } from '@objectstack/spec/system'; + +export const arObjects: NonNullable = { + sys_user: { + label: "المستخدم", + pluralLabel: "المستخدمون", + description: "حسابات المستخدمين للمصادقة", + fields: { + name: { + label: "الاسم" + }, + email: { + label: "البريد الإلكتروني" + }, + email_verified: { + label: "تم التحقق من البريد الإلكتروني" + }, + two_factor_enabled: { + label: "المصادقة الثنائية مُفعَّلة", + help: "ما إذا كانت المصادقة الثنائية مُفعَّلة لهذا المستخدم. تتولى إضافة `twoFactor` في better-auth صيانة هذه القيمة." + }, + role: { + label: "دور المنصة", + help: "قيمة الدور القديمة في better-auth (admin، user، …). لم يعد ObjectStack يكتبها. لمنح صلاحية مسؤول المنصة، أدرِج البريد الإلكتروني الموثَّق للمستخدم في `OS_PLATFORM_OWNER_EMAIL`؛ وفي وضعية المستأجرين `single` يمنحها أيضًا إسناد `admin_full_access` غير المقيَّد بنطاق في `sys_user_permission_set`." + }, + banned: { + label: "محظور", + help: "عند التفعيل، لا يستطيع المستخدم تسجيل الدخول. استخدم إجراءَي «حظر المستخدم» و«إلغاء حظر المستخدم» لتغييرها." + }, + ban_reason: { + label: "سبب الحظر" + }, + ban_expires: { + label: "انتهاء الحظر", + help: "عند تعيينه، يُرفع الحظر تلقائيًا في هذا الوقت." + }, + failed_login_count: { + label: "عدد محاولات الدخول الفاشلة", + help: "محاولات تسجيل الدخول الفاشلة المتتالية؛ يُعاد تعيينها إلى 0 عند النجاح. يتولى مدير المصادقة صيانتها." + }, + locked_until: { + label: "مقفل حتى", + help: "عند تعيينه بوقت في المستقبل، يُرفض تسجيل الدخول (قفل ضد هجمات التخمين). يُرفع تلقائيًا بعد هذا الوقت، ويمكن للمسؤول رفعه مبكرًا عبر «فتح القفل»." + }, + password_changed_at: { + label: "تاريخ تغيير كلمة المرور", + help: "الطابع الزمني لآخر تغيير لكلمة المرور. يستند إليه password_expiry_days؛ يديره النظام." + }, + phone_number: { + label: "رقم الهاتف", + help: "رقم الهاتف المستخدم لتسجيل الدخول (يُوصى بتنسيق E.164). فريد لكل مستخدم؛ يديره better-auth عند تفعيل إضافة phoneNumber." + }, + phone_number_verified: { + label: "تم التحقق من الهاتف", + help: "ما إذا كان رقم الهاتف قد تم التحقق منه (يتطلب التحقق برمز OTP بنية رسائل نصية؛ تبقى القيمة false إلى أن تتوفر). يديره النظام." + }, + must_change_password: { + label: "يجب تغيير كلمة المرور", + help: "عند التفعيل، يجب على المستخدم تغيير كلمة المرور قبل أن يتمكن من المتابعة. يُفعَّل عندما ينشئ المسؤول الحساب أو يستورده أو يعيد تعيين كلمة مروره؛ يديره النظام." + }, + mfa_required_at: { + label: "تاريخ فرض المصادقة متعددة العوامل", + help: "وقت تطبيق المصادقة متعددة العوامل الإلزامية على هذا المستخدم لأول مرة (بداية مهلة السماح). يستند إليه mfa_required؛ يديره النظام." + }, + last_login_at: { + label: "آخر تسجيل دخول", + help: "الطابع الزمني لآخر تسجيل دخول ناجح. يسجّله مدير المصادقة؛ يديره النظام." + }, + last_login_ip: { + label: "عنوان IP لآخر تسجيل دخول", + help: "عنوان IP للعميل في آخر تسجيل دخول ناجح (من ترويسة التمرير الخاصة بالوكيل الموثوق). يديره النظام." + }, + ai_access: { + label: "الوصول إلى الذكاء الاصطناعي", + help: "ما إذا كان هذا المستخدم يشغل مقعد ذكاء اصطناعي. يمنح المقعد صلاحية `ai_seat` التي تفتح مساعدي الذكاء الاصطناعي في التطبيق (البناء والسؤال). يحدّ عدد المقاعد المضمّنة في ترخيصك عدد المستخدمين الذين يمكنهم شغل مقعد." + }, + image: { + label: "صورة الملف الشخصي" + }, + locale: { + label: "اللغة", + help: "اللغة التي يرى بها هذا المستخدم الواجهة ويتلقى بها الإشعارات، على هيئة وسم لغة مثل zh-CN أو ja-JP. عند عدم تعيينها، تُطبَّق اللغة الافتراضية للنشر." + }, + manager_id: { + label: "المدير", + help: "المدير المباشر لهذا المستخدم. يكوّن سلسلة التبعية الإدارية التي يمشي عليها نطاق التسلسل الهرمي `own_and_reports`." + }, + primary_business_unit_id: { + label: "وحدة الأعمال الأساسية", + help: "وحدة الأعمال الأساسية للمستخدم — إسقاط غير مُطبَّع لـ sys_business_unit_member.is_primary تتولى plugin-sharing صيانته. يتيح لحقل البحث عن المستخدمين تصفية المرشحين حسب وحدة الأعمال دون المرور بجدول العضوية الوسيط. لا تعدّله مباشرة؛ اضبطه عبر عضوية وحدة الأعمال." + }, + source: { + label: "مصدر الهوية", + help: "طريقة إنشاء هذه الهوية — idp_provisioned (توفير فوري عبر SSO الموحَّد) أو env_native (تسجيل محلي / مستخدم نهائي للتطبيق). يديره النظام؛ لا تعدّله.", + options: { + idp_provisioned: "مُوفَّر من مزوّد الهوية", + env_native: "حساب محلي" + } + }, + id: { + label: "معرّف المستخدم" + }, + created_at: { + label: "تاريخ الإنشاء" + }, + updated_at: { + label: "تاريخ التحديث" + } + }, + _views: { + all_users: { + label: "جميع المستخدمين" + }, + me: { + label: "ملفي الشخصي" + }, + unverified: { + label: "غير مُتحقَّق منه" + }, + banned: { + label: "محظور" + } + }, + _actions: { + invite_user: { + label: "دعوة مستخدم", + description: "ادعُ شخصًا عبر عنوان بريده الإلكتروني. ينضم إلى هذه المؤسسة بالدور المختار عندما يقبل الدعوة.", + successMessage: "أُرسلت الدعوة إلى {{ result.email }}", + outcomeMessages: { + sent: "أُرسلت الدعوة إلى {{ result.email }}", + link_only: "أُنشئت دعوة لـ {{ result.email }}. البريد غير مُهيَّأ على هذا الخادم، لذا لم تُرسَل أي رسالة. استخدم «نسخ رابط الدعوة» في الدعوة لمشاركتها." + }, + params: { + role: { + helpText: "المالك: تحكم كامل في المؤسسة، بما في ذلك حذفها.\nالمسؤول: يدير الأعضاء والدعوات والفرق وإعدادات المؤسسة.\nالمسؤول المفوَّض: يدعو الأشخاص ويضعهم فقط حيث فوّضه المسؤول بذلك.\nالعضو: يعمل في تطبيقات المؤسسة بالوصول الذي تمنحه مجموعات أذوناته.\nالخارجي: شخص من خارج المؤسسة، مثل مورّد أو عميل، لا يرى إلا ما تتم مشاركته معه.", + options: { + owner: "المالك", + admin: "المسؤول", + delegated_admin: "المسؤول المفوَّض", + member: "العضو", + external: "خارجي" + } + } + } + }, + ban_user: { + label: "حظر المستخدم", + description: "هل تريد حظر هذا المستخدم؟ سيُسجَّل خروجه ولن يتمكن من تسجيل الدخول، وستُرفض مفاتيح API الخاصة به إلى أن يُلغى الحظر، وكذلك رموز الوصول عبر OAuth وتحديثات الرموز. تعود المفاتيح للعمل بعد إلغاء الحظر.", + successMessage: "تم حظر المستخدم", + params: { + banReason: { + label: "سبب الحظر" + } + } + }, + unban_user: { + label: "إلغاء حظر المستخدم", + successMessage: "تم إلغاء حظر المستخدم" + }, + unlock_user: { + label: "فتح قفل الحساب", + successMessage: "تم فتح قفل الحساب" + }, + create_user: { + label: "إنشاء مستخدم", + successMessage: "تم إنشاء المستخدم", + params: { + phoneNumber: { + label: "رقم الهاتف", + helpText: "رقم الهاتف المستخدم لتسجيل الدخول (بتنسيق E.164، مثل +8613800000000). مطلوب عند عدم إدخال بريد إلكتروني." + }, + password: { + label: "كلمة المرور (اتركها فارغة لتوليدها)" + }, + mustChangePassword: { + label: "طلب تغيير كلمة المرور عند أول تسجيل دخول" + }, + permissionSet: { + label: "مجموعة الأذونات", + helpText: "اختياري. يمنح المستخدم الجديد مجموعة الأذونات هذه، حتى يتمكن من فتح تطبيقاته عند أول تسجيل دخول." + } + }, + resultDialog: { + title: "تم إنشاء المستخدم", + acknowledge: "تم", + fields: { + "user.email": "البريد الإلكتروني", + "user.phoneNumber": "رقم الهاتف", + permissionSet: "مجموعة الأذونات", + temporaryPassword: "كلمة المرور المؤقتة: انسخها الآن، فهي تُعرض مرة واحدة فقط ولا تُخزَّن أبدًا" + } + } + }, + set_user_password: { + label: "تعيين كلمة المرور", + successMessage: "تم تحديث كلمة المرور", + params: { + newPassword: { + label: "كلمة المرور الجديدة (اتركها فارغة لتوليدها)" + }, + mustChangePassword: { + label: "طلب تغيير كلمة المرور عند تسجيل الدخول التالي" + }, + revokeSessionsAndKeys: { + label: "تسجيل الخروج من كل مكان وإبطال مفاتيح API", + helpText: "يُنهي كل جلسات هذا المستخدم ويُبطل كل مفاتيح API التي يملكها. أبقِه مفعّلًا إذا كان الحساب قد يكون مُخترَقًا، وأوقفه عند إعادة التعيين الروتينية. تبقى المفاتيح المُبطَلة مُبطَلة بعد تغيير كلمة المرور." + } + }, + resultDialog: { + title: "تم تحديث كلمة المرور", + acknowledge: "تم", + fields: { + temporaryPassword: "كلمة المرور المؤقتة: انسخها الآن، فهي تُعرض مرة واحدة فقط ولا تُخزَّن أبدًا" + } + } + }, + impersonate_user: { + label: "انتحال صفة المستخدم", + confirmText: "هل تريد بدء جلسة انتحال صفة لهذا المستخدم؟ استخدمها فقط في حالات الدعم المشروعة — ستُسجَّل الإجراءات.", + successMessage: "أنت الآن تنتحل صفة المستخدم" + }, + set_user_manager: { + label: "تعيين المدير", + description: "عيِّن مدير هذا المستخدم. تحدد سلسلة التبعية الإدارية توجيه الموافقات ونطاق السجلات own_and_reports.", + successMessage: "تم تحديث المدير", + params: { + managerId: { + label: "المدير", + helpText: "المستخدم الذي يتبع له هذا الشخص. يرفض الخادم مديرًا من خارج مؤسسته، وتعيين الشخص مديرًا لنفسه، وأي ربط من شأنه أن يُغلق سلسلة التبعية الإدارية في حلقة أو يزيد عمقها عن الحد." + } + } + }, + list_platform_admins: { + label: "مسؤولو المنصة", + resultDialog: { + title: "مسؤولو المنصة", + description: "كل من يملك صلاحية مسؤول المنصة حاليًا: منحة admin_full_access سارية ضمن نافذتها الزمنية تملكها المؤسسة الافتراضية، أو عنوان موثَّق في قائمة مالكي النشر.", + acknowledge: "إغلاق", + fields: { + entries: "مسؤولو المنصة" + } + } + }, + update_my_profile: { + label: "تحديث الملف الشخصي", + successMessage: "تم تحديث الملف الشخصي" + }, + change_my_password: { + label: "تغيير كلمة المرور", + successMessage: "تم تغيير كلمة المرور", + params: { + currentPassword: { + label: "كلمة المرور الحالية" + }, + newPassword: { + label: "كلمة المرور الجديدة" + }, + revokeOtherSessions: { + label: "تسجيل الخروج من الأجهزة الأخرى" + } + } + }, + change_my_email: { + label: "تغيير البريد الإلكتروني", + successMessage: "أُرسلت رسالة تحقق — راجع العنوان الجديد للتأكيد.", + params: { + newEmail: { + label: "البريد الإلكتروني الجديد" + } + } + }, + resend_verification_email: { + label: "إعادة إرسال رسالة التحقق", + successMessage: "أُرسلت رسالة تحقق — راجع صندوق الوارد." + }, + delete_my_account: { + label: "حذف حسابي", + description: "هل تريد حذف حسابك نهائيًا؟ لا يمكن التراجع عن ذلك — ستُنهى جميع جلساتك وستُزال جميع البيانات التي تملكها وفق سياسة الاحتفاظ المُهيَّأة.", + successMessage: "تم حذف الحساب", + params: { + password: { + label: "كلمة المرور الحالية" + } + } + }, + enable_two_factor: { + label: "تفعيل المصادقة الثنائية", + params: { + password: { + label: "كلمة المرور الحالية" + } + }, + resultDialog: { + title: "تم تفعيل المصادقة الثنائية", + description: "امسح رمز QR بتطبيق المصادقة، ثم احفظ رموز النسخ الاحتياطي في مكان آمن. تُعرض رموز النسخ الاحتياطي مرة واحدة فقط.", + acknowledge: "لقد حفظت رموز النسخ الاحتياطي", + fields: { + totpURI: "معرّف URI للمصادِق", + backupCodes: "رموز النسخ الاحتياطي" + } + } + }, + disable_two_factor: { + label: "تعطيل المصادقة الثنائية", + description: "هل تريد إيقاف المصادقة الثنائية؟ سيصبح حسابك أقل أمانًا.", + successMessage: "تم تعطيل المصادقة الثنائية.", + params: { + password: { + label: "كلمة المرور الحالية" + } + } + }, + generate_backup_codes: { + label: "إعادة توليد رموز النسخ الاحتياطي", + description: "هل تريد توليد مجموعة جديدة من رموز النسخ الاحتياطي؟ ستتوقف أي رموز وُلِّدت سابقًا عن العمل.", + params: { + password: { + label: "كلمة المرور الحالية" + } + }, + resultDialog: { + title: "تم توليد رموز نسخ احتياطي جديدة", + description: "رموز النسخ الاحتياطي السابقة لم تعد صالحة. احفظ هذه الرموز الجديدة في مكان آمن — فهي تُعرض مرة واحدة فقط.", + acknowledge: "لقد حفظت الرموز الجديدة", + fields: { + backupCodes: "رموز النسخ الاحتياطي" + } + } + } + }, + _validations: { + locale_bcp47_shape: { + message: "يجب أن تكون اللغة وسم لغة بصيغة BCP-47، مثل zh-CN أو ja-JP." + } + } + }, + sys_session: { + label: "الجلسة", + pluralLabel: "الجلسات", + description: "جلسات المستخدمين النشطة", + fields: { + user_id: { + label: "المستخدم" + }, + expires_at: { + label: "تاريخ الانتهاء" + }, + last_activity_at: { + label: "آخر نشاط", + help: "الطابع الزمني لآخر طلب في هذه الجلسة؛ تستند إليه مهلة الخمول. يديره النظام." + }, + revoked_at: { + label: "تاريخ الإلغاء", + help: "عند تعيينه، تكون هذه الجلسة قد أُلغيت (خمول / تجاوز الحد الأقصى المطلق / حد الجلسات المتزامنة / المسؤول / انتهاء العضوية في المؤسسة). يديره النظام." + }, + revoke_reason: { + label: "سبب الإلغاء", + help: "سبب إلغاء الجلسة (idle_timeout، absolute_max، concurrent_cap، user_revoked، admin، organization_membership_ended، admin_password_reset، password_reset، …)." + }, + active_organization_id: { + label: "المؤسسة النشطة" + }, + active_team_id: { + label: "الفريق النشط" + }, + ip_address: { + label: "عنوان IP" + }, + user_agent: { + label: "وكيل المستخدم" + }, + impersonated_by: { + label: "انتحل الصفة", + help: "معرّف المستخدم للمسؤول الذي بدأ جلسة انتحال الصفة هذه، إن وُجد." + }, + token: { + label: "رمز الجلسة", + help: "رمز جلسة معتم — لا يُعرض أبدًا في الواجهة" + }, + id: { + label: "معرّف الجلسة" + }, + created_at: { + label: "تاريخ الإنشاء" + }, + updated_at: { + label: "تاريخ التحديث" + } + }, + _views: { + all_sessions: { + label: "الكل" + }, + mine: { + label: "جلساتي" + }, + revoked: { + label: "مُلغاة" + } + }, + _actions: { + revoke_my_other_sessions: { + label: "تسجيل الخروج من الأجهزة الأخرى", + confirmText: "هل تريد تسجيل الخروج من كل الأجهزة الأخرى التي سجّلت الدخول عليها حاليًا؟ ستبقى جلستك الحالية نشطة.", + successMessage: "تم إلغاء كل الجلسات الأخرى" + }, + revoke_session: { + label: "إلغاء الجلسة", + confirmText: "هل تريد إلغاء هذه الجلسة؟ سيُسجَّل خروج المستخدم من ذلك الجهاز.", + successMessage: "تم إلغاء الجلسة" + } + } + }, + sys_account: { + label: "الحساب", + pluralLabel: "الحسابات", + description: "حسابات OAuth وحسابات مزوّدي المصادقة", + fields: { + id: { + label: "معرّف الحساب" + }, + display_title: { + label: "العنوان", + help: "عنوان السجل: المزوّد ومعرّف الحساب الذي أصدره (يُحسب عند القراءة)" + }, + created_at: { + label: "تاريخ الإنشاء" + }, + updated_at: { + label: "تاريخ التحديث" + }, + provider_id: { + label: "معرّف المزوّد", + help: "معرّف مزوّد OAuth (google، github، إلخ)" + }, + account_id: { + label: "معرّف الحساب لدى المزوّد", + help: "معرّف المستخدم في نظام المزوّد" + }, + user_id: { + label: "المستخدم", + help: "رابط إلى جدول المستخدمين" + }, + access_token: { + label: "رمز الوصول", + help: "رمز وصول OAuth ساري المفعول أصدره المزوّد — لا تُعيده واجهة البيانات أبدًا؛ يقرؤه better-auth عبر أداة الوصول المميَّزة إلى الحقول الداخلية في المحرك" + }, + refresh_token: { + label: "رمز التحديث", + help: "رمز تحديث OAuth ساري المفعول — طويل الأمد ولا يُبطله إلغاء جلسة ObjectStack؛ لا تُعيده واجهة البيانات أبدًا" + }, + id_token: { + label: "رمز الهوية (ID Token)", + help: "رمز هوية OIDC أصدره المزوّد — لا تُعيده واجهة البيانات أبدًا" + }, + access_token_expires_at: { + label: "انتهاء رمز الوصول" + }, + refresh_token_expires_at: { + label: "انتهاء رمز التحديث" + }, + scope: { + label: "نطاق OAuth" + }, + password: { + label: "تجزئة كلمة المرور", + help: "كلمة المرور المُجزَّأة لمزوّد البريد الإلكتروني/كلمة المرور" + }, + previous_password_hashes: { + label: "تجزئات كلمات المرور السابقة", + help: "مصفوفة JSON بتجزئات كلمات المرور السابقة (يحدّها password_history_count)؛ لمنع إعادة الاستخدام فقط. يديره النظام." + } + }, + _views: { + all_links: { + label: "الكل" + }, + mine: { + label: "روابطي" + }, + by_provider: { + label: "حسب المزوّد" + } + }, + _actions: { + unlink_account: { + label: "إلغاء ربط الحساب", + description: "هل تريد إلغاء ربط الهوية هذا؟ لن يتمكن المستخدم من تسجيل الدخول عبر هذا المزوّد إلى أن يعيد ربطه.", + successMessage: "تمت إزالة ربط الهوية" + } + } + }, + sys_verification: { + label: "التحقق", + pluralLabel: "عمليات التحقق", + description: "رموز التحقق من البريد الإلكتروني والهاتف", + fields: { + id: { + label: "معرّف التحقق" + }, + display_title: { + label: "العنوان", + help: "عنوان السجل: تسمية ثابتة (عمودا المعرّف والرمز داخليان ولا يُستخدمان عنوانًا أبدًا)" + }, + created_at: { + label: "تاريخ الإنشاء" + }, + updated_at: { + label: "تاريخ التحديث" + }, + value: { + label: "رمز التحقق", + help: "الرمز أو الشيفرة المستخدمة للتحقق" + }, + expires_at: { + label: "تاريخ الانتهاء" + }, + identifier: { + label: "المعرّف", + help: "عنوان البريد الإلكتروني أو رقم الهاتف" + } + } + }, + sys_organization: { + label: "المؤسسة", + pluralLabel: "المؤسسات", + description: "المؤسسات لتجميع المستأجرين المتعددين", + fields: { + name: { + label: "الاسم", + help: "الاسم الخاص بالمؤسسة. غيّره عبر «إعادة تسمية المؤسسة» أعلى هذا السجل. اسم المنتج الذي تعرضه وحدة التحكم إعداد مختلف، يُضبط في الإعدادات ← العلامة التجارية." + }, + slug: { + label: "المُعرِّف النصي (Slug)", + help: "معرّف مناسب للاستخدام في عناوين URL" + }, + logo: { + label: "الشعار" + }, + metadata: { + label: "البيانات الوصفية", + help: "البيانات الوصفية للمؤسسة بصيغة JSON مُسلسَلة" + }, + require_mfa: { + label: "طلب المصادقة متعددة العوامل (أُوقف العمل به)", + help: "أُوقف العمل به. اضبط متطلب المصادقة متعددة العوامل لهذه المؤسسة في الإعدادات ← سياسة الوصول. القيمة التي كانت مفعّلة هنا نُقلت إلى هناك بوصفها «الجميع» ثم مُسحت." + }, + timezone: { + label: "المنطقة الزمنية", + help: "منطقة زمنية بصيغة IANA (مثل UTC أو Asia/Shanghai)، تُحفظ كقيمة افتراضية جذرية لوحدات الأعمال التي ليست لها منطقة خاصة بها. لا تغيّر حاليًا أي حساب للتواريخ: تستخدم الصيغ والتقارير والتواريخ المعروضة الإعدادات ← الترجمة والإعدادات المحلية ← المنطقة الزمنية الافتراضية." + }, + parent_organization_id: { + label: "المؤسسة الأم", + help: "الأصل لأغراض التقارير/التجميع. لا يمنح أي شيء — تأتي الرؤية عبر المؤسسات من العضوية، ولا تأتي أبدًا من هذا المرجع. ذو معنى فقط حيث يشغّل النشر عدة مؤسسات." + }, + sort_order: { + label: "ترتيب الفرز", + help: "ترتيب العرض بين المؤسسات الشقيقة. للعرض فقط. ذو معنى فقط حيث يشغّل النشر عدة مؤسسات." + }, + id: { + label: "معرّف المؤسسة" + }, + created_at: { + label: "تاريخ الإنشاء" + }, + updated_at: { + label: "تاريخ التحديث" + } + }, + _views: { + all_orgs: { + label: "الكل" + } + }, + _actions: { + invite_user: { + label: "دعوة مستخدم", + description: "ادعُ شخصًا عبر عنوان بريده الإلكتروني. ينضم إلى هذه المؤسسة بالدور المختار عندما يقبل الدعوة.", + successMessage: "أُرسلت الدعوة إلى {{ result.email }}", + outcomeMessages: { + sent: "أُرسلت الدعوة إلى {{ result.email }}", + link_only: "أُنشئت دعوة لـ {{ result.email }}. البريد غير مُهيَّأ على هذا الخادم، لذا لم تُرسَل أي رسالة. استخدم «نسخ رابط الدعوة» في الدعوة لمشاركتها." + }, + params: { + role: { + helpText: "المالك: تحكم كامل في المؤسسة، بما في ذلك حذفها.\nالمسؤول: يدير الأعضاء والدعوات والفرق وإعدادات المؤسسة.\nالمسؤول المفوَّض: يدعو الأشخاص ويضعهم فقط حيث فوّضه المسؤول بذلك.\nالعضو: يعمل في تطبيقات المؤسسة بالوصول الذي تمنحه مجموعات أذوناته.\nالخارجي: شخص من خارج المؤسسة، مثل مورّد أو عميل، لا يرى إلا ما تتم مشاركته معه.", + options: { + owner: "المالك", + admin: "المسؤول", + delegated_admin: "المسؤول المفوَّض", + member: "العضو", + external: "خارجي" + } + } + } + }, + create_organization: { + label: "إنشاء مؤسسة", + successMessage: "تم إنشاء المؤسسة" + }, + update_organization: { + label: "تعديل المؤسسة", + successMessage: "تم تحديث المؤسسة" + }, + delete_organization: { + label: "حذف المؤسسة", + confirmText: "هل تريد حذف هذه المؤسسة؟ سيفقد جميع الأعضاء الوصول فورًا. لا يمكن التراجع عن ذلك.", + successMessage: "تم حذف المؤسسة" + }, + set_active_organization: { + label: "تعيين كنشطة", + successMessage: "تم تبديل المؤسسة النشطة" + }, + rename_organization: { + label: "إعادة تسمية المؤسسة", + successMessage: "تمت إعادة تسمية المؤسسة" + }, + leave_organization: { + label: "مغادرة المؤسسة", + confirmText: "هل تريد مغادرة هذه المؤسسة؟ ستفقد الوصول إلى بيئاتها وأعضائها.", + successMessage: "لقد غادرت المؤسسة" + } + }, + _sections: { + identity: { + label: "الهوية" + }, + branding: { + label: "العلامة التجارية" + }, + configuration: { + label: "التهيئة" + }, + group_structure: { + label: "بنية المجموعة" + } + } + }, + sys_member: { + label: "العضو", + pluralLabel: "الأعضاء", + description: "سجلات العضوية في المؤسسة", + fields: { + id: { + label: "معرّف العضو" + }, + display_title: { + label: "العنوان", + help: "عنوان السجل: العضو، ودوره عند تسجيله (يُحسب عند القراءة)" + }, + created_at: { + label: "تاريخ الإنشاء" + }, + organization_id: { + label: "المؤسسة" + }, + user_id: { + label: "المستخدم" + }, + role: { + label: "الدور", + help: "دور العضو داخل المؤسسة", + options: { + owner: "المالك", + admin: "المسؤول", + delegated_admin: "المسؤول المفوَّض", + member: "العضو", + external: "خارجي" + } + } + }, + _views: { + mine: { + label: "عضوياتي", + emptyState: { + title: "لا توجد مؤسسات بعد", + message: "لم تنضم إلى أي مؤسسة." + } + } + }, + _actions: { + invite_user: { + label: "دعوة مستخدم", + description: "ادعُ شخصًا عبر عنوان بريده الإلكتروني. ينضم إلى هذه المؤسسة بالدور المختار عندما يقبل الدعوة.", + successMessage: "أُرسلت الدعوة إلى {{ result.email }}", + outcomeMessages: { + sent: "أُرسلت الدعوة إلى {{ result.email }}", + link_only: "أُنشئت دعوة لـ {{ result.email }}. البريد غير مُهيَّأ على هذا الخادم، لذا لم تُرسَل أي رسالة. استخدم «نسخ رابط الدعوة» في الدعوة لمشاركتها." + }, + params: { + role: { + helpText: "المالك: تحكم كامل في المؤسسة، بما في ذلك حذفها.\nالمسؤول: يدير الأعضاء والدعوات والفرق وإعدادات المؤسسة.\nالمسؤول المفوَّض: يدعو الأشخاص ويضعهم فقط حيث فوّضه المسؤول بذلك.\nالعضو: يعمل في تطبيقات المؤسسة بالوصول الذي تمنحه مجموعات أذوناته.\nالخارجي: شخص من خارج المؤسسة، مثل مورّد أو عميل، لا يرى إلا ما تتم مشاركته معه.", + options: { + owner: "المالك", + admin: "المسؤول", + delegated_admin: "المسؤول المفوَّض", + member: "العضو", + external: "خارجي" + } + } + } + }, + add_member: { + label: "إضافة عضو", + successMessage: "تمت إضافة العضو", + params: { + role: { + helpText: "المالك: تحكم كامل في المؤسسة، بما في ذلك حذفها.\nالمسؤول: يدير الأعضاء والدعوات والفرق وإعدادات المؤسسة.\nالمسؤول المفوَّض: يدعو الأشخاص ويضعهم فقط حيث فوّضه المسؤول بذلك.\nالعضو: يعمل في تطبيقات المؤسسة بالوصول الذي تمنحه مجموعات أذوناته.\nالخارجي: شخص من خارج المؤسسة، مثل مورّد أو عميل، لا يرى إلا ما تتم مشاركته معه." + } + } + }, + update_member_role: { + label: "تغيير الدور", + description: "اختر الدور الجديد في المؤسسة لـ {{ record.user_id.name | default:'هذا العضو' }}.", + successMessage: "تم تحديث دور العضو", + outcomeMessages: { + permission_set_granted: "تم تحديث دور العضو. ومنح الدور الجديد أيضًا مجموعة الأذونات {{ result.grantedPermissionSet.label }}." + }, + params: { + role: { + helpText: "المالك: تحكم كامل في المؤسسة، بما في ذلك حذفها.\nالمسؤول: يدير الأعضاء والدعوات والفرق وإعدادات المؤسسة.\nالمسؤول المفوَّض: يدعو الأشخاص ويضعهم فقط حيث فوّضه المسؤول بذلك.\nالعضو: يعمل في تطبيقات المؤسسة بالوصول الذي تمنحه مجموعات أذوناته.\nالخارجي: شخص من خارج المؤسسة، مثل مورّد أو عميل، لا يرى إلا ما تتم مشاركته معه." + } + } + }, + remove_member: { + label: "إزالة العضو", + confirmText: "هل تريد إزالة هذا العضو من المؤسسة؟ سيفقد الوصول إلى جميع موارد المؤسسة.", + successMessage: "تمت إزالة العضو" + }, + transfer_ownership: { + label: "تعيين كمالك", + confirmText: "هل تريد جعل العضو المحدد مالكًا لهذه المؤسسة؟ سيحصل على كل امتيازات المالك، بما في ذلك حذف المؤسسة. وتبقى أنت مالكًا أيضًا؛ للتنحي، استخدم «تغيير الدور» على عضويتك.", + successMessage: "أصبح العضو مالكًا", + outcomeMessages: { + permission_set_granted: "أصبح العضو مالكًا. ومنح الدور الجديد أيضًا مجموعة الأذونات {{ result.grantedPermissionSet.label }}." + } + } + } + }, + sys_invitation: { + label: "الدعوة", + pluralLabel: "الدعوات", + description: "دعوات المؤسسة لإلحاق المستخدمين", + fields: { + id: { + label: "معرّف الدعوة" + }, + display_title: { + label: "العنوان", + help: "عنوان السجل: البريد الإلكتروني المدعو (يُحسب عند القراءة)" + }, + created_at: { + label: "تاريخ الإنشاء" + }, + organization_id: { + label: "المؤسسة" + }, + email: { + label: "البريد الإلكتروني", + help: "البريد الإلكتروني للمستخدم المدعو" + }, + role: { + label: "الدور", + help: "الدور الذي يُسنَد عند القبول", + options: { + owner: "المالك", + admin: "المسؤول", + delegated_admin: "المسؤول المفوَّض", + member: "العضو", + external: "خارجي" + } + }, + status: { + label: "الحالة", + options: { + pending: "قيد الانتظار", + accepted: "مقبولة", + rejected: "مرفوضة", + expired: "منتهية الصلاحية", + canceled: "ملغاة" + } + }, + inviter_id: { + label: "الداعي", + help: "المستخدم الذي أرسل الدعوة" + }, + expires_at: { + label: "تاريخ الانتهاء" + }, + team_id: { + label: "الفريق", + help: "فريق اختياري يُسنَد عند القبول" + }, + business_unit_id: { + label: "وحدة الأعمال للتعيين", + help: "وحدة الأعمال التي يوضع تحتها المدعو عند القبول. يجب أن تقع داخل الشجرة الفرعية المفوَّضة لمُصدِر الدعوة." + }, + positions: { + label: "المناصب للتعيين", + help: "أسماء المناصب التي تُسنَد عند القبول. يجب أن يكون كل مجموعات أذونات المنصب مسموحًا بها في adminScope لمُصدِر الدعوة." + } + }, + _views: { + pending: { + label: "قيد الانتظار", + emptyState: { + title: "لا توجد دعوات قيد الانتظار", + message: "تبقى الدعوة هنا إلى أن تُقبل أو تُرفض أو تُلغى، أو إلى أن تنتهي صلاحيتها." + } + }, + accepted: { + label: "مقبولة", + emptyState: { + title: "لا توجد دعوات مقبولة", + message: "تنتقل الدعوة إلى هنا عندما يقبلها الشخص وينضم إلى المؤسسة." + } + }, + expired: { + label: "منتهية الصلاحية / ملغاة", + emptyState: { + title: "لا توجد دعوات منتهية الصلاحية أو ملغاة", + message: "تُحفظ هنا الدعوات التي انتهت صلاحيتها أو رُفضت أو أُلغيت." + } + }, + all_invitations: { + label: "الكل", + emptyState: { + title: "لا توجد دعوات بعد", + message: "تُدرج هنا كل دعوة للانضمام إلى مؤسسة، أيًّا كانت حالتها." + } + } + }, + _actions: { + invite_user: { + label: "دعوة مستخدم", + description: "ادعُ شخصًا عبر عنوان بريده الإلكتروني. ينضم إلى هذه المؤسسة بالدور المختار عندما يقبل الدعوة.", + successMessage: "أُرسلت الدعوة إلى {{ result.email }}", + outcomeMessages: { + sent: "أُرسلت الدعوة إلى {{ result.email }}", + link_only: "أُنشئت دعوة لـ {{ result.email }}. البريد غير مُهيَّأ على هذا الخادم، لذا لم تُرسَل أي رسالة. استخدم «نسخ رابط الدعوة» في الدعوة لمشاركتها." + }, + params: { + role: { + helpText: "المالك: تحكم كامل في المؤسسة، بما في ذلك حذفها.\nالمسؤول: يدير الأعضاء والدعوات والفرق وإعدادات المؤسسة.\nالمسؤول المفوَّض: يدعو الأشخاص ويضعهم فقط حيث فوّضه المسؤول بذلك.\nالعضو: يعمل في تطبيقات المؤسسة بالوصول الذي تمنحه مجموعات أذوناته.\nالخارجي: شخص من خارج المؤسسة، مثل مورّد أو عميل، لا يرى إلا ما تتم مشاركته معه.", + options: { + owner: "المالك", + admin: "المسؤول", + delegated_admin: "المسؤول المفوَّض", + member: "العضو", + external: "خارجي" + } + } + } + }, + cancel_invitation: { + label: "إلغاء الدعوة", + confirmText: "هل تريد إلغاء هذه الدعوة؟ لن يتمكن المستلم من قبولها بعد ذلك.", + successMessage: "تم إلغاء الدعوة", + cancelText: "الإبقاء على الدعوة" + }, + resend_invitation: { + label: "إعادة إرسال الدعوة", + description: "هل تريد إعادة إرسال الدعوة إلى {{ record.email }}؟ يبدأ تاريخ انتهاء الصلاحية من جديد، وتُستبدل الدعوة المنتهية الصلاحية بدعوة جديدة.", + successMessage: "تمت إعادة إرسال الدعوة", + outcomeMessages: { + sent: "أُعيد إرسال الدعوة إلى {{ result.email }}", + link_only: "جُدِّدت الدعوة لـ {{ result.email }}. البريد غير مُهيَّأ على هذا الخادم، لذا لم تُرسَل أي رسالة. استخدم «نسخ رابط الدعوة» في الدعوة لمشاركتها." + } + }, + copy_invitation_link: { + label: "نسخ رابط الدعوة", + resultDialog: { + title: "رابط الدعوة", + description: "يمكن لأي شخص يفتح هذا الرابط قبول الدعوة، لذا شاركه فقط مع الشخص الذي دعوته. يتوقف عن العمل بمجرد قبول الدعوة أو رفضها أو إلغائها أو انتهاء صلاحيتها.", + acknowledge: "تم", + fields: { + acceptUrl: "رابط الدعوة" + } + } + }, + accept_invitation: { + label: "قبول الدعوة", + successMessage: "تم قبول الدعوة" + }, + reject_invitation: { + label: "رفض الدعوة", + confirmText: "هل تريد رفض هذه الدعوة؟ سيُبلَّغ الداعي، وستحتاج إلى دعوة جديدة للانضمام.", + successMessage: "تم رفض الدعوة" + } + } + }, + sys_team: { + label: "الفريق", + pluralLabel: "الفرق", + description: "فرق داخل المؤسسات لتجميع أدق", + fields: { + name: { + label: "الاسم" + }, + organization_id: { + label: "المؤسسة", + help: "المؤسسة الأم لهذا الفريق. فارغة في وضع المستأجر الواحد؛ تُختم تلقائيًا في وضع المستأجرين المتعددين." + }, + member_count: { + label: "عدد الأعضاء", + help: "عدّاد المقاعد الذي يتولى better-auth صيانته؛ لا تكتب فيه مباشرة." + }, + id: { + label: "معرّف الفريق" + }, + created_at: { + label: "تاريخ الإنشاء" + }, + updated_at: { + label: "تاريخ التحديث" + } + }, + _views: { + by_org: { + label: "حسب المؤسسة" + }, + all_teams: { + label: "الكل" + } + }, + _actions: { + create_team: { + label: "إنشاء فريق", + successMessage: "تم إنشاء الفريق" + }, + update_team: { + label: "تعديل الفريق", + successMessage: "تم تحديث الفريق" + }, + add_team_member: { + label: "إضافة عضو", + successMessage: "تمت إضافة عضو الفريق" + }, + remove_team: { + label: "حذف الفريق", + confirmText: "هل تريد حذف هذا الفريق؟ سيفقد الأعضاء أي وصول مقيَّد بنطاق الفريق. لا يمكن التراجع عن ذلك.", + successMessage: "تم حذف الفريق" + } + } + }, + sys_team_member: { + label: "عضو الفريق", + pluralLabel: "أعضاء الفريق", + description: "سجلات العضوية في الفرق التي تربط المستخدمين بالفرق", + fields: { + id: { + label: "معرّف عضو الفريق" + }, + display_title: { + label: "العنوان", + help: "عنوان السجل: المستخدم والفريق (يُحسب عند القراءة)" + }, + created_at: { + label: "تاريخ الإنشاء" + }, + team_id: { + label: "الفريق" + }, + user_id: { + label: "المستخدم" + }, + membership_key: { + label: "مفتاح العضوية", + help: "ملخّص عضوية مشتق يتولى better-auth صيانته؛ لا تكتب فيه مباشرة." + } + }, + _actions: { + add_team_member: { + label: "إضافة عضو", + successMessage: "تمت إضافة عضو الفريق" + }, + remove_team_member: { + label: "إزالة من الفريق", + description: "هل تريد إزالة هذا المستخدم من الفريق؟ سيفقد أي وصول مقيَّد بنطاق الفريق.", + successMessage: "تمت إزالة عضو الفريق" + } + } + }, + sys_business_unit: { + label: "وحدة الأعمال", + pluralLabel: "وحدات الأعمال", + description: "هيكل شركتك — القطاعات والأقسام والمناطق والمكاتب. يمكن لقواعد المشاركة مشاركة السجلات مع وحدة، أو مع وحدة والوحدات التابعة لها: يحصل الأعضاء على ذلك الوصول، ويفقدونه عند مغادرتهم. أما السماح لمجموعة أذونات بقراءة كل سجلات وحدة (نطاق القراءة 'unit') فيتطلب إصدار المؤسسات.", + fields: { + name: { + label: "الاسم" + }, + code: { + label: "الرمز", + help: "رمز قصير ثابت (مثل EMEA-SALES). فريد داخل المستأجر." + }, + kind: { + label: "النوع", + help: "للتجميع وإعداد التقارير فقط؛ لا يغيّر موضع الوحدة في المؤسسة ولا من تشملهم.", + options: { + company: "شركة", + division: "قطاع", + department: "قسم", + office: "مكتب", + cost_center: "مركز تكلفة" + } + }, + parent_business_unit_id: { + label: "وحدة الأعمال الأم", + help: "الوحدة التي تقع هذه الوحدة تحتها. اتركه فارغًا لوحدة من المستوى الأعلى. لا يمكن حذف وحدة لا تزال تحتها وحدات إلى أن تُنقل تحت وحدة أخرى أو تُحذف." + }, + timezone: { + label: "المنطقة الزمنية", + help: "منطقة زمنية بصيغة IANA (مثل Asia/Shanghai) تُحفظ لهذه الوحدة؛ اتركها فارغة لتأخذ منطقة الوحدة الأم، ثم منطقة المؤسسة. لا تغيّر حاليًا أي حساب للتواريخ: تستخدم الصيغ والتقارير والتواريخ المعروضة الإعدادات ← الترجمة والإعدادات المحلية ← المنطقة الزمنية الافتراضية." + }, + organization_id: { + label: "المؤسسة", + help: "نطاق المستأجر. فارغ في وضع المستأجر الواحد؛ يُختم تلقائيًا في وضع المستأجرين المتعددين." + }, + manager_user_id: { + label: "رئيس وحدة الأعمال", + help: "المستخدم المسؤول عن هذه الوحدة التنظيمية (رئيس / قائد وحدة الأعمال)." + }, + active: { + label: "نشط", + help: "أوقفه لإحالة الوحدة إلى التقاعد: المشاركة وأي وصول آخر مُنح لهذه الوحدة، أو لوحدة فوقها، لن يصل بعد ذلك إلى أعضائها ولا إلى الوحدات التابعة لها." + }, + effective_from: { + label: "ساري من", + help: "تاريخ نشوء وحدة الأعمال هذه. يُملأ عادةً عبر المزامنة من نظام الموارد البشرية لديك." + }, + effective_to: { + label: "ساري حتى", + help: "تاريخ إحالة وحدة الأعمال هذه إلى التقاعد. يُملأ عادةً عبر المزامنة من نظام الموارد البشرية لديك." + }, + external_ref: { + label: "المرجع الخارجي", + help: "معرّف الوحدة في نظام الموارد البشرية لديك (مثل Workday أو SAP HR أو 北森)، ويُستخدم لمطابقتها في كل مزامنة." + }, + id: { + label: "معرّف وحدة الأعمال" + }, + created_at: { + label: "تاريخ الإنشاء" + }, + updated_at: { + label: "تاريخ التحديث" + } + }, + _views: { + org_chart: { + label: "الهيكل التنظيمي" + }, + active: { + label: "نشط" + }, + inactive: { + label: "غير نشط" + }, + by_kind: { + label: "حسب النوع" + }, + all_departments: { + label: "الكل" + } + } + }, + sys_business_unit_member: { + label: "عضو وحدة الأعمال", + pluralLabel: "أعضاء وحدة الأعمال", + description: "إسناد مستخدم إلى وحدة أعمال (مناسب للمؤسسات المصفوفية، ومحدد بتواريخ سريان).", + fields: { + id: { + label: "معرّف العضو" + }, + display_title: { + label: "العنوان", + help: "عنوان السجل: المستخدم ووحدة الأعمال المُسنَد إليها (يُحسب عند القراءة)" + }, + business_unit_id: { + label: "وحدة الأعمال" + }, + user_id: { + label: "المستخدم" + }, + function_in_business_unit: { + label: "الوظيفة في وحدة الأعمال", + help: "`lead` هو الرئيس في العمل اليومي؛ أما `deputy` فيمكنه أن ينوب عن الرئيس في توجيه الموافقات.", + options: { + member: "عضو", + lead: "رئيس", + deputy: "نائب" + } + }, + is_primary: { + label: "الإسناد الأساسي", + help: "عندما يكون المستخدم في عدة وحدات أعمال، يحدد هذا الوحدة المعتمدة لإعداد التقارير." + }, + effective_from: { + label: "ساري من" + }, + effective_to: { + label: "ساري حتى" + }, + created_at: { + label: "تاريخ الإنشاء" + }, + updated_at: { + label: "تاريخ التحديث" + } + } + }, + sys_api_key: { + label: "مفتاح API", + pluralLabel: "مفاتيح API", + description: "مفاتيح API للوصول البرمجي", + fields: { + name: { + label: "الاسم", + help: "تسمية مقروءة لمفتاح API" + }, + prefix: { + label: "البادئة", + help: "بادئة ظاهرة لتعريف المفتاح (مثل \"osk_\")" + }, + user_id: { + label: "المالك", + help: "المستخدم الذي يملك مفتاح API هذا" + }, + active_organization_id: { + label: "المؤسسة النشطة", + help: "المؤسسة التي يصادق هذا المفتاح للدخول إليها — تُورَث من منشئ المفتاح عند الإنشاء وتُعتمد بوصفها المؤسسة النشطة للطلب" + }, + scopes: { + label: "النطاقات", + help: "مصفوفة JSON بنطاقات الأذونات" + }, + expires_at: { + label: "تاريخ الانتهاء" + }, + last_used_at: { + label: "آخر استخدام", + help: "يُسجَّل عند استخدام المفتاح، مرة واحدة في الساعة على الأكثر. يُرفض المفتاح الذي لم يُسجَّل له استخدام خلال نافذة عدم النشاط (90 يومًا افتراضيًا) إلى أن يُستبدل" + }, + revoked: { + label: "مُلغاة" + }, + revoked_reason: { + label: "سبب الإلغاء", + help: "يُعيَّن عندما تُبطل إعادة تعيين كلمة المرور القسرية من قِبل المسؤول هذا المفتاح. لا يستطيع استعادة مثل هذا المفتاح إلا مسؤول المنصة؛ وينشئ مالكه مفتاحًا جديدًا. يُمسح عند استعادة المفتاح. فارغ للمفتاح المُبطَل من قائمة مفاتيح API.", + options: { + admin_password_reset: "إعادة تعيين كلمة المرور من قِبل المسؤول" + } + }, + key: { + label: "المفتاح المُجزَّأ", + help: "القيمة المُجزَّأة لمفتاح API — لا تُكشف أبدًا للعملاء" + }, + id: { + label: "معرّف مفتاح API" + }, + created_at: { + label: "تاريخ الإنشاء" + }, + updated_at: { + label: "تاريخ التحديث" + } + }, + _views: { + all_keys: { + label: "الكل", + emptyState: { + title: "لا توجد مفاتيح API بعد", + message: "أنشئ مفتاحًا للبرامج النصية والتكاملات عبر «إنشاء مفتاح»؛ يُعرض المفتاح مرة واحدة، عند إنشائه." + } + }, + mine: { + label: "مفاتيحي", + emptyState: { + title: "ليست لديك مفاتيح API بعد", + message: "أنشئ مفتاحًا لبرامجك النصية وتكاملاتك عبر «إنشاء مفتاح»؛ يُعرض المفتاح مرة واحدة، عند إنشائه." + } + }, + active: { + label: "نشط", + emptyState: { + title: "لا توجد مفاتيح API نشطة", + message: "تُدرج هنا كل المفاتيح التي لم تُبطَل." + } + }, + revoked: { + label: "مُلغاة", + emptyState: { + title: "لا توجد مفاتيح مُبطَلة", + message: "ينتقل المفتاح إلى هنا عند إبطاله؛ ويفقد العملاء الذين يستخدمونه الوصول إلى أن تتم استعادته." + } + } + }, + _actions: { + create_api_key: { + label: "إنشاء مفتاح", + description: "أنشئ مفتاحًا لبرنامج نصي أو تكامل. يعمل المفتاح باسمك، وبصلاحيات وصولك، في المؤسسة التي تعمل فيها الآن.", + params: { + expires_at: { + helpText: "اتركه فارغًا لاستخدام مدة الصلاحية الافتراضية المضبوطة في الإعداد ← المصادقة ← مفاتيح API (مدة صلاحية المفتاح الافتراضية، 90 يومًا ما لم تُغيَّر). يُرفض أي تاريخ يتجاوز الحد الأقصى لمدة صلاحية المفتاح هناك." + } + }, + resultDialog: { + title: "تم إنشاء مفتاح API", + description: "انسخ المفتاح الآن — فهو يُعرض مرة واحدة فقط ولا يمكن استرداده. إذا تسرّب، فأبطله وأنشئ مفتاحًا جديدًا.", + acknowledge: "لقد حفظت المفتاح", + fields: { + key: "مفتاح API", + expires_at: "تاريخ الانتهاء" + } + } + }, + revoke_api_key: { + label: "إبطال مفتاح API", + confirmText: "هل تريد إبطال مفتاح API هذا؟ سيفقد أي عميل يستخدمه الوصول فورًا.", + successMessage: "تم إبطال مفتاح API \"{{ record.name }}\"" + }, + restore_api_key: { + label: "استعادة مفتاح API", + confirmText: "هل تريد استعادة مفتاح API المُبطَل هذا؟ سيستعيد العملاء الحاليون الذين يحملون المفتاح الوصول.", + successMessage: "تمت استعادة مفتاح API \"{{ record.name }}\"" + } + } + }, + sys_two_factor: { + label: "المصادقة الثنائية", + pluralLabel: "بيانات اعتماد المصادقة الثنائية", + description: "بيانات اعتماد المصادقة الثنائية", + fields: { + id: { + label: "معرّف المصادقة الثنائية" + }, + display_title: { + label: "العنوان", + help: "عنوان السجل: المستخدم الذي تخصه بيانات الاعتماد (يُحسب عند القراءة)" + }, + created_at: { + label: "تاريخ الإنشاء" + }, + updated_at: { + label: "تاريخ التحديث" + }, + user_id: { + label: "المستخدم" + }, + secret: { + label: "السر", + help: "المفتاح السري لـ TOTP" + }, + backup_codes: { + label: "رموز النسخ الاحتياطي", + help: "رموز الاسترداد الاحتياطية، مُشفَّرة أثناء التخزين (نص مُشفَّر واحد معتم، وليس JSON قابلًا للقراءة)" + }, + verified: { + label: "مُتحقَّق منه", + help: "ما إذا كان التسجيل قد تأكد برمز TOTP صالح (يديره better-auth)" + }, + failed_verification_count: { + label: "عدد مرات التحقق الفاشلة", + help: "عمليات التحقق الثنائي الفاشلة المتتالية؛ يُعاد تعيينها عند النجاح. يتولى better-auth صيانتها." + }, + locked_until: { + label: "مقفل حتى", + help: "يُعيَّن عندما تتجاوز عمليات التحقق الثنائي الفاشلة حدّ القفل. يتولى better-auth صيانته." + } + }, + _views: { + mine: { + label: "تسجيلي" + }, + all_enrollments: { + label: "الكل" + } + }, + _actions: { + enable_two_factor: { + label: "تفعيل المصادقة الثنائية", + params: { + password: { + label: "كلمة المرور الحالية" + } + }, + resultDialog: { + title: "تم تفعيل المصادقة الثنائية", + description: "امسح رمز QR بتطبيق المصادقة، ثم احفظ رموز النسخ الاحتياطي في مكان آمن. تُعرض رموز النسخ الاحتياطي مرة واحدة فقط.", + acknowledge: "لقد حفظت رموز النسخ الاحتياطي", + fields: { + totpURI: "معرّف URI للمصادِق", + backupCodes: "رموز النسخ الاحتياطي" + } + } + }, + disable_two_factor: { + label: "تعطيل المصادقة الثنائية", + description: "هل تريد تعطيل المصادقة الثنائية على حسابك؟", + successMessage: "تم تعطيل المصادقة الثنائية", + params: { + password: { + label: "كلمة المرور الحالية" + } + } + }, + regenerate_backup_codes: { + label: "إعادة توليد رموز النسخ الاحتياطي", + description: "هل تريد إعادة توليد رموز النسخ الاحتياطي؟ ستتوقف كل رموز النسخ الاحتياطي السابقة عن العمل فورًا.", + params: { + password: { + label: "كلمة المرور الحالية" + } + }, + resultDialog: { + title: "تم توليد رموز نسخ احتياطي جديدة", + description: "رموز النسخ الاحتياطي السابقة لم تعد صالحة. احفظ هذه الرموز الجديدة في مكان آمن — فهي تُعرض مرة واحدة فقط.", + acknowledge: "لقد حفظت الرموز الجديدة", + fields: { + backupCodes: "رموز النسخ الاحتياطي" + } + } + } + } + }, + sys_device_code: { + label: "رمز الجهاز", + pluralLabel: "رموز الأجهزة", + description: "الطلبات المعلّقة لمنح تفويض الجهاز في OAuth 2.0 (RFC 8628)", + fields: { + id: { + label: "معرّف رمز الجهاز" + }, + created_at: { + label: "تاريخ الإنشاء" + }, + updated_at: { + label: "تاريخ التحديث" + }, + device_code: { + label: "رمز الجهاز", + help: "رمز عالي العشوائية يُعاد إلى الجهاز الذي يستطلع" + }, + user_code: { + label: "رمز المستخدم", + help: "رمز قصير يظهر للمستخدم (مثل ABCD-EFGH)" + }, + user_id: { + label: "المستخدم", + help: "المستخدم الذي وافق على تفويض الجهاز" + }, + expires_at: { + label: "تاريخ الانتهاء", + help: "وقت انتهاء صلاحية رمزي الجهاز والمستخدم" + }, + status: { + label: "الحالة", + help: "الحالة الحالية: 'pending' | 'approved' | 'denied'" + }, + last_polled_at: { + label: "آخر استطلاع", + help: "الطابع الزمني لآخر استطلاع لـ /device/token" + }, + polling_interval: { + label: "فاصل الاستطلاع (مللي ثانية)", + help: "الحد الأدنى لفاصل الاستطلاع الذي يوصي به الخادم، بالمللي ثانية" + }, + client_id: { + label: "معرّف العميل", + help: "معرّف عميل OAuth للجهاز الطالب" + }, + scope: { + label: "النطاق", + help: "نطاقات OAuth التي يطلبها الجهاز، مفصولة بمسافات" + } + } + }, + sys_user_preference: { + label: "تفضيل المستخدم", + pluralLabel: "تفضيلات المستخدم", + description: "تفضيلات لكل مستخدم بصيغة مفتاح-قيمة (السمة، اللغة، إلخ)", + fields: { + id: { + label: "معرّف التفضيل" + }, + created_at: { + label: "تاريخ الإنشاء" + }, + updated_at: { + label: "تاريخ التحديث" + }, + user_id: { + label: "المستخدم", + help: "المستخدم المالك لهذا التفضيل" + }, + key: { + label: "المفتاح", + help: "مفتاح التفضيل (مثل theme، locale، plugin.ai.auto_save)" + }, + value: { + label: "القيمة", + help: "قيمة التفضيل (أي نوع قابل للتسلسل بصيغة JSON)" + } + }, + _views: { + all_preferences: { + label: "الكل" + }, + mine: { + label: "تفضيلاتي" + }, + by_user: { + label: "حسب المستخدم" + } + } + }, + sys_personal_view: { + label: "طريقة عرض شخصية", + pluralLabel: "طرق العرض الشخصية", + description: "طرق عرض القوائم المُسمّاة الخاصة بالقارئ (طرق العرض الخاصة بي). مرئية لمالكها فقط.", + fields: { + id: { + label: "معرّف طريقة العرض الشخصية" + }, + created_at: { + label: "تاريخ الإنشاء" + }, + updated_at: { + label: "تاريخ التحديث" + }, + user_id: { + label: "المالك", + help: "المستخدم الذي تخصه طريقة العرض هذه. لا يقرؤها أو يغيّرها سواه." + }, + object_name: { + label: "الكائن", + help: "الكائن الذي تعرض طريقة العرض هذه قائمته (اسمه البرمجي)." + }, + label: { + label: "الاسم", + help: "الاسم الذي أعطاه المالك لطريقة العرض هذه." + }, + config: { + label: "تهيئة طريقة العرض", + help: "تهيئة القائمة، بشكل ListView في المواصفة. يُتحقق منها عند كل حفظ." + } + } + }, + sys_oauth_application: { + label: "تطبيق OAuth", + pluralLabel: "تطبيقات OAuth", + description: "تطبيقات عملاء OAuth/OIDC المسجَّلة", + fields: { + id: { + label: "المعرّف" + }, + name: { + label: "الاسم" + }, + icon: { + label: "الأيقونة", + help: "رابط الشعار المعروض على شاشة الموافقة" + }, + uri: { + label: "معرّف URI للصفحة الرئيسية", + help: "الصفحة الرئيسية العامة للعميل المسجَّل" + }, + contacts: { + label: "جهات الاتصال", + help: "قائمة عناوين البريد الإلكتروني لجهات الاتصال بصيغة JSON مُسلسَلة" + }, + tos: { + label: "شروط الخدمة" + }, + policy: { + label: "سياسة الخصوصية" + }, + metadata: { + label: "البيانات الوصفية", + help: "البيانات الوصفية للتطبيق بصيغة JSON مُسلسَلة" + }, + client_id: { + label: "معرّف العميل", + help: "معرّف عميل OAuth العام" + }, + client_discovery_id: { + label: "معرّف اكتشاف العميل", + help: "معرّف معتم يستخدمه المزوّد للبحث عن هذا العميل على مسار الاكتشاف، ويُبقى منفصلًا عن `client_id` العام" + }, + client_secret: { + label: "سر العميل", + help: "سر عميل OAuth — يُخزَّن كملخّص SHA-256، ولا يُخزَّن أبدًا كنص صريح (`storeClientSecret` في `@better-auth/oauth-provider`، الذي يكون مُجزَّأً افتراضيًا كلما كانت إضافة jwt مفعّلة؛ موصول في `AuthManager.buildPluginList()` ضمن plugin-auth، فرع oidcProvider). يُعرض مرة واحدة عند التسجيل." + }, + redirect_uris: { + label: "معرّفات URI لإعادة التوجيه", + help: "قائمة معرّفات URI المسموح بها لإعادة التوجيه بصيغة JSON مُسلسَلة" + }, + post_logout_redirect_uris: { + label: "معرّفات URI لإعادة التوجيه بعد تسجيل الخروج", + help: "قائمة معرّفات URI المسموح بها لإعادة التوجيه بعد تسجيل الخروج بصيغة JSON مُسلسَلة" + }, + type: { + label: "نوع العميل", + options: { + web: "ويب", + native: "أصلي" + } + }, + public: { + label: "عميل عام", + help: "يحدد العميل كعميل OAuth عام (غير سري)" + }, + require_pkce: { + label: "طلب PKCE" + }, + token_endpoint_auth_method: { + label: "طريقة المصادقة لنقطة نهاية الرمز", + help: "مثل client_secret_basic، client_secret_post، none" + }, + grant_types: { + label: "أنواع المنح", + help: "قائمة أنواع المنح المسموح بها بصيغة JSON مُسلسَلة" + }, + response_types: { + label: "أنواع الاستجابة", + help: "قائمة أنواع الاستجابة المسموح بها بصيغة JSON مُسلسَلة" + }, + scopes: { + label: "النطاقات المسموح بها", + help: "قائمة النطاقات التي يجوز للعميل طلبها بصيغة JSON مُسلسَلة" + }, + client_credentials_scopes: { + label: "نطاقات بيانات اعتماد العميل", + help: "قائمة النطاقات التي يجوز للعميل طلبها في منح client_credentials، حيث لا يوجد مستخدم ليوافق، بصيغة JSON مُسلسَلة — وتُبقى منفصلة عن `scopes` التي تحكم المنح المفوَّضة من المستخدم" + }, + subject_type: { + label: "نوع الموضوع", + help: "نوع موضوع OIDC (مثل public، pairwise)" + }, + jwks: { + label: "JWKS", + help: "مجموعة مفاتيح JSON Web Key الخاصة بالعميل (للتحقق عبر private_key_jwt / الطلبات الموقَّعة)" + }, + jwks_uri: { + label: "JWKS URI", + help: "رابط مجموعة مفاتيح JSON Web Key الخاصة بالعميل" + }, + dpop_bound_access_tokens: { + label: "رموز وصول مرتبطة بـ DPoP", + help: "إلزام أن تكون رموز الوصول الصادرة لهذا العميل مرتبطة بـ DPoP (RFC 9449)" + }, + disabled: { + label: "معطَّل" + }, + skip_consent: { + label: "تخطي الموافقة", + help: "معاملته كعميل موثوق وتجاوز شاشة الموافقة" + }, + enable_end_session: { + label: "تفعيل إنهاء الجلسة", + help: "السماح للعميل باستدعاء نقطة نهاية إنهاء الجلسة في OIDC" + }, + backchannel_logout_uri: { + label: "معرّف URI لتسجيل الخروج عبر القناة الخلفية", + help: "نقطة نهاية تسجيل الخروج عبر القناة الخلفية في OIDC الخاصة بالعميل" + }, + backchannel_logout_session_required: { + label: "طلب الجلسة في تسجيل الخروج عبر القناة الخلفية", + help: "ما إذا كان يجب أن يتضمن رمز تسجيل الخروج عبر القناة الخلفية مطالبة sid" + }, + software_id: { + label: "معرّف البرنامج" + }, + software_version: { + label: "إصدار البرنامج" + }, + software_statement: { + label: "بيان البرنامج", + help: "رمز JWT موقَّع يؤكد البيانات الوصفية للعميل (RFC 7591 §2.3)" + }, + user_id: { + label: "المستخدم المالك", + help: "المستخدم الذي سجّل هذا التطبيق" + }, + reference_id: { + label: "المعرّف المرجعي", + help: "معرّف ارتباط يقدّمه المستدعي" + }, + created_at: { + label: "تاريخ الإنشاء" + }, + updated_at: { + label: "تاريخ التحديث" + } + }, + _views: { + all_apps: { + label: "الكل", + emptyState: { + title: "لا توجد تطبيقات OAuth بعد", + message: "سجّل تطبيقًا ليتمكن عملاؤه من تسجيل دخول المستخدمين عبر هذا النظام." + } + }, + mine: { + label: "تطبيقاتي", + emptyState: { + title: "ليست لديك تطبيقات OAuth بعد", + message: "تُدرج هنا التطبيقات التي تسجّلها." + } + }, + active: { + label: "نشط", + emptyState: { + title: "لا توجد تطبيقات OAuth نشطة", + message: "يبقى التطبيق نشطًا إلى أن يُعطَّل؛ وتُدرج التطبيقات المعطَّلة ضمن «المعطَّلة»." + } + }, + disabled_apps: { + label: "معطَّل", + emptyState: { + title: "لا توجد تطبيقات OAuth معطَّلة", + message: "ينتقل التطبيق إلى هنا عند تعطيله؛ ولا يستطيع عملاؤه تسجيل الدخول إلى أن يُفعَّل مجددًا." + } + } + }, + _actions: { + disable_oauth_application: { + label: "تعطيل تطبيق OAuth", + description: "هل تريد تعطيل تطبيق OAuth هذا؟ ستظل رموز الوصول/التحديث النشطة الصادرة له مرفوضة عند نقاط نهاية الرمز والتفويض والفحص. ستتوقف التكاملات الحالية عن العمل فورًا.", + successMessage: "تم تعطيل تطبيق OAuth" + }, + enable_oauth_application: { + label: "تفعيل تطبيق OAuth", + description: "هل تريد إعادة تفعيل تطبيق OAuth هذا؟ سيُستأنف إصدار الرموز والتفويض والفحص فورًا.", + successMessage: "تم تفعيل تطبيق OAuth" + }, + create_oauth_application: { + label: "تسجيل تطبيق OAuth", + params: { + name: { + label: "اسم التطبيق" + }, + redirectURLs: { + label: "عناوين URL لإعادة التوجيه", + helpText: "عنوان URL واحد في كل سطر. تحتاج تطبيقات الويب إلى https:// على مضيف غير localhost أو أي عنوان استرجاع آخر. ويجوز للتطبيقات الأصلية أيضًا استخدام http:// على localhost أو 127.0.0.1 أو [::1] تحديدًا، وعلى أي منفذ." + }, + type: { + label: "نوع التطبيق", + helpText: "اختر «أصلي» لتطبيق سطح مكتب أو جوال، أو لتطبيق قيد التطوير المحلي يعيد التوجيه إلى http://localhost.", + options: { + web: "ويب", + native: "أصلي" + } + } + }, + resultDialog: { + title: "تم تسجيل تطبيق OAuth", + description: "احفظ client_secret الآن — فهو يُعرض مرة واحدة فقط ولا يمكن استرداده. يمكنك تدويره لاحقًا إذا تسرّب.", + acknowledge: "لقد حفظت سر العميل", + fields: { + "client.client_id": "معرّف العميل", + "client.client_secret": "سر العميل" + } + } + }, + rotate_client_secret: { + label: "تدوير سر العميل", + description: "هل تريد تدوير سر عميل OAuth هذا؟ سيتوقف السر السابق عن العمل فورًا، وستتعطل أي تكاملات تستخدمه إلى أن تُحدَّث بالسر الجديد. يُعرض السر الجديد مرة واحدة فقط.", + resultDialog: { + title: "تم تدوير سر العميل", + description: "احفظ السر الجديد الآن — فهو يُعرض مرة واحدة فقط. توقف السر السابق عن العمل بالفعل: حدِّث كل تكامل بالسر الجديد.", + acknowledge: "لقد حدّثت تكاملاتي", + fields: { + client_secret: "سر العميل الجديد" + } + } + }, + delete_oauth_application: { + label: "حذف تطبيق OAuth", + description: "هل تريد حذف تطبيق OAuth هذا نهائيًا؟ ستُبطَل كل الرموز والموافقات الصادرة، وستتوقف التكاملات التي تستخدم client_id هذا عن العمل فورًا. لا يمكن التراجع عن ذلك.", + successMessage: "تم حذف تطبيق OAuth" + } + } + }, + sys_oauth_access_token: { + label: "رمز وصول OAuth", + pluralLabel: "رموز وصول OAuth", + description: "رموز وصول OAuth المعتمة الصادرة لتطبيقات العملاء", + fields: { + id: { + label: "المعرّف" + }, + token: { + label: "الرمز", + help: "قيمة رمز الوصول المعتمة" + }, + client_id: { + label: "معرّف العميل", + help: "معرّف العميل لتطبيق OAuth الذي صدر له هذا الرمز." + }, + session_id: { + label: "الجلسة", + help: "جلسة تسجيل الدخول التي صدر فيها هذا الرمز." + }, + user_id: { + label: "المستخدم", + help: "المستخدم الذي يعمل هذا الرمز نيابةً عنه." + }, + refresh_id: { + label: "رمز التحديث", + help: "رمز التحديث الذي صدر منه رمز الوصول هذا." + }, + reference_id: { + label: "المعرّف المرجعي", + help: "المؤسسة المرتبطة بالموافقة التي صدر هذا الرمز بموجبها؛ فارغة عند عدم ربط أي مؤسسة" + }, + authorization_code_id: { + label: "معرّف رمز التفويض", + help: "معرّف منح رمز التفويض الذي نشأ منه هذا الرمز" + }, + resources: { + label: "الموارد", + help: "قائمة مؤشرات الموارد وفق RFC 8707 المرتبطة بهذا الرمز بصيغة JSON مُسلسَلة" + }, + requested_user_info_claims: { + label: "مطالبات UserInfo المطلوبة", + help: "قائمة مطالبات OIDC المطلوبة لنقطة نهاية userinfo بصيغة JSON مُسلسَلة" + }, + scopes: { + label: "النطاقات", + help: "قائمة النطاقات الممنوحة لهذا الرمز بصيغة JSON مُسلسَلة" + }, + expires_at: { + label: "تاريخ الانتهاء" + }, + created_at: { + label: "تاريخ الإنشاء" + }, + revoked: { + label: "تاريخ الإلغاء", + help: "الطابع الزمني الذي أُبطل فيه رمز الوصول هذا" + }, + confirmation: { + label: "التأكيد", + help: "مطالبة cnf بصيغة JSON وفق RFC 7800 (مثل بصمة مفتاح DPoP) تربط هذا الرمز بمفتاح" + } + } + }, + sys_oauth_refresh_token: { + label: "رمز تحديث OAuth", + pluralLabel: "رموز تحديث OAuth", + description: "رموز تحديث OAuth المعتمة (مرتبطة بجلسة)", + fields: { + id: { + label: "المعرّف" + }, + token: { + label: "الرمز", + help: "قيمة رمز التحديث المعتمة" + }, + client_id: { + label: "معرّف العميل", + help: "معرّف العميل لتطبيق OAuth الذي صدر له هذا الرمز." + }, + session_id: { + label: "الجلسة", + help: "جلسة تسجيل الدخول التي صدر فيها هذا الرمز." + }, + user_id: { + label: "المستخدم", + help: "المستخدم الذي يعمل هذا الرمز نيابةً عنه." + }, + reference_id: { + label: "المعرّف المرجعي", + help: "المؤسسة المرتبطة بالموافقة التي بدأت عائلة التحديث هذه؛ فارغة عند عدم ربط أي مؤسسة" + }, + authorization_code_id: { + label: "معرّف رمز التفويض", + help: "معرّف منح رمز التفويض الذي نشأت منه سلسلة الرموز هذه" + }, + resources: { + label: "الموارد", + help: "قائمة مؤشرات الموارد وفق RFC 8707 المرتبطة بهذا الرمز بصيغة JSON مُسلسَلة" + }, + requested_user_info_claims: { + label: "مطالبات UserInfo المطلوبة", + help: "قائمة مطالبات OIDC المطلوبة لنقطة نهاية userinfo بصيغة JSON مُسلسَلة" + }, + scopes: { + label: "النطاقات", + help: "قائمة النطاقات الممنوحة لهذا الرمز بصيغة JSON مُسلسَلة" + }, + expires_at: { + label: "تاريخ الانتهاء" + }, + created_at: { + label: "تاريخ الإنشاء" + }, + revoked: { + label: "تاريخ الإلغاء", + help: "الطابع الزمني الذي أُبطل فيه رمز التحديث هذا" + }, + rotated_at: { + label: "تاريخ التدوير", + help: "الطابع الزمني الذي جرى فيه تدوير هذا الرمز (حلّ محله صف جديد)" + }, + rotation_replay_response: { + label: "استجابة إعادة التشغيل عند التدوير", + help: "استجابة رمز مخزَّنة مؤقتًا تُعاد عند تقديم الرمز القديم مجددًا ضمن فترة إعادة الاستخدام" + }, + rotation_replay_expires_at: { + label: "انتهاء إعادة التشغيل عند التدوير", + help: "نهاية فترة إعادة الاستخدام بعد التدوير التي تُقدَّم خلالها استجابة إعادة التشغيل" + }, + auth_time: { + label: "وقت المصادقة", + help: "الوقت الذي صادق فيه المستخدم أصلًا لسلسلة الرموز هذه" + }, + confirmation: { + label: "التأكيد", + help: "مطالبة cnf بصيغة JSON وفق RFC 7800 (مثل بصمة مفتاح DPoP) تربط هذا الرمز بمفتاح" + } + } + }, + sys_oauth_consent: { + label: "موافقة OAuth", + pluralLabel: "موافقات OAuth", + description: "سجلات موافقة المستخدمين لتطبيقات عملاء OAuth", + fields: { + id: { + label: "المعرّف" + }, + client_id: { + label: "معرّف العميل", + help: "معرّف العميل لتطبيق OAuth الذي وافق عليه المستخدم." + }, + user_id: { + label: "المستخدم", + help: "المستخدم الذي منح هذه الموافقة." + }, + reference_id: { + label: "المعرّف المرجعي", + help: "المؤسسة التي تربطها هذه الموافقة: المؤسسة النشطة للمستخدم الموافق عند الموافقة؛ فارغة عند عدم وجود مؤسسة نشطة" + }, + resources: { + label: "الموارد", + help: "قائمة مؤشرات الموارد وفق RFC 8707 التي تشملها الموافقة بصيغة JSON مُسلسَلة" + }, + requested_user_info_claims: { + label: "مطالبات UserInfo المطلوبة", + help: "قائمة مطالبات OIDC التي وافق المستخدم على كشفها بصيغة JSON مُسلسَلة" + }, + scopes: { + label: "النطاقات", + help: "قائمة النطاقات التي وافق عليها المستخدم بصيغة JSON مُسلسَلة" + }, + created_at: { + label: "تاريخ الإنشاء" + }, + updated_at: { + label: "تاريخ التحديث" + }, + last_used_at: { + label: "آخر استخدام", + help: "بداية آخر ساعة أجرى فيها العميل استدعاء MCP مُتحقَّقًا منه بموجب هذه الموافقة؛ فارغة عند عدم تسجيل أي استدعاء" + } + } + }, + sys_oauth_resource: { + label: "مورد OAuth", + pluralLabel: "موارد OAuth", + description: "موارد OAuth المحمية المسجَّلة (مؤشرات الموارد وفق RFC 8707)", + fields: { + id: { + label: "المعرّف" + }, + identifier: { + label: "المعرّف", + help: "معرّف URI لمؤشر المورد المقدَّم في معامل resource وفق RFC 8707" + }, + name: { + label: "الاسم" + }, + access_token_ttl: { + label: "مدة صلاحية رمز الوصول (TTL)", + help: "مدة صلاحية رمز الوصول بالثواني لهذا المورد (تتجاوز القيمة الافتراضية للخادم)" + }, + refresh_token_ttl: { + label: "مدة صلاحية رمز التحديث (TTL)", + help: "مدة صلاحية رمز التحديث بالثواني لهذا المورد (تتجاوز القيمة الافتراضية للخادم)" + }, + signing_algorithm: { + label: "خوارزمية التوقيع", + help: "خوارزمية JWS المستخدمة لتوقيع رموز الوصول لهذا المورد" + }, + signing_key_id: { + label: "معرّف مفتاح التوقيع", + help: "معرّف المفتاح (kid) المستخدم لتوقيع رموز الوصول لهذا المورد" + }, + allowed_scopes: { + label: "النطاقات المسموح بها", + help: "قائمة النطاقات التي يجوز للعملاء طلبها لهذا المورد بصيغة JSON مُسلسَلة" + }, + custom_claims: { + label: "المطالبات المخصّصة", + help: "كائن JSON بمطالبات إضافية تُختم على رموز الوصول لهذا المورد" + }, + dpop_bound_access_tokens_required: { + label: "طلب DPoP", + help: "إلزام أن تكون رموز الوصول لهذا المورد مرتبطة بـ DPoP (RFC 9449)" + }, + disabled: { + label: "معطَّل" + }, + policy_version: { + label: "إصدار السياسة", + help: "إصدار متزايد رتيبًا لسياسة رموز المورد" + }, + metadata: { + label: "البيانات الوصفية", + help: "كائن JSON ببيانات وصفية إضافية للمورد" + }, + created_at: { + label: "تاريخ الإنشاء" + }, + updated_at: { + label: "تاريخ التحديث" + } + } + }, + sys_oauth_client_resource: { + label: "مورد عميل OAuth", + pluralLabel: "موارد عملاء OAuth", + description: "منح تسمح لعميل OAuth بطلب رموز لمورد محمي", + fields: { + id: { + label: "المعرّف" + }, + client_id: { + label: "معرّف العميل", + help: "معرّف العميل لتطبيق OAuth الذي يخصه هذا المنح." + }, + resource_id: { + label: "معرّف المورد", + help: "معرّف المورد المحمي الذي يجوز للتطبيق طلب رموز له." + }, + metadata: { + label: "البيانات الوصفية", + help: "كائن JSON ببيانات وصفية إضافية للمنح" + }, + created_at: { + label: "تاريخ الإنشاء" + } + } + }, + sys_oauth_client_assertion: { + label: "تأكيد عميل OAuth", + pluralLabel: "تأكيدات عملاء OAuth", + description: "معرّفات JTI المستهلكة لتأكيدات عملاء OAuth (منع إعادة التشغيل وفق RFC 7523)", + fields: { + id: { + label: "المعرّف" + }, + expires_at: { + label: "تاريخ الانتهاء", + help: "انتهاء صلاحية التأكيد — يمكن حذف الصفوف التي تجاوزت هذه اللحظة بأمان" + } + } + }, + sys_jwks: { + label: "مفتاح JWKS", + pluralLabel: "مفاتيح JWKS", + description: "أزواج مفاتيح غير متماثلة تُستخدم لتوقيع رموز JWT الصادرة والتحقق منها", + fields: { + id: { + label: "معرّف المفتاح", + help: "قيمة `kid` في JWK" + }, + public_key: { + label: "المفتاح العام", + help: "المفتاح العام بصيغة JWK في JSON مُسلسَل" + }, + private_key: { + label: "المفتاح الخاص", + help: "المفتاح الخاص بصيغة JWK في JSON مُسلسَل (مُشفَّر أثناء التخزين)" + }, + alg: { + label: "الخوارزمية", + help: "خوارزمية توقيع JWK، مثل `EdDSA` (better-auth 1.7+)" + }, + crv: { + label: "المنحنى", + help: "منحنى JWK لمفاتيح EdDSA/EC، مثل `Ed25519` (better-auth 1.7+)" + }, + created_at: { + label: "تاريخ الإنشاء" + }, + expires_at: { + label: "تاريخ الانتهاء", + help: "الوقت الذي لا يعود فيه المفتاح صالحًا للتحقق من الرموز" + } + } + }, + sys_sso_provider: { + label: "مزوّد SSO", + pluralLabel: "مزوّدو SSO", + description: "مزوّدو هوية SSO الخارجيون (OIDC / SAML) الذين يفوّض إليهم هذا النظام تسجيل الدخول", + fields: { + id: { + label: "المعرّف" + }, + provider_id: { + label: "معرّف المزوّد", + help: "معرّف مزوّد ثابت (فريد داخل البيئة)" + }, + issuer: { + label: "المُصدِر", + help: "رابط مُصدِر مزوّد الهوية" + }, + domain: { + label: "نطاق البريد الإلكتروني", + help: "نطاق البريد الإلكتروني الموجَّه إلى مزوّد الهوية هذا (مثل acme.com)" + }, + domain_verified: { + label: "تم التحقق من النطاق", + help: "ما إذا كانت ملكية DNS لنطاق البريد الإلكتروني قد ثبتت. يضبطه «التحقق من النطاق» بعد أن يُحَلّ سجل DNS من نوع TXT. يديره better-auth — ولا يمكن تعديله مباشرة. لا يُفرض إلا عند تفعيل التحقق من النطاق للبيئة." + }, + oidc_config: { + label: "تهيئة OIDC", + help: "JSON: clientId، clientSecret، endpoints، scopes، mapping، pkce (يديرها better-auth)" + }, + saml_config: { + label: "تهيئة SAML", + help: "JSON: entryPoint، cert، identifierFormat، mapping (يديرها better-auth)" + }, + user_id: { + label: "سجّله", + help: "المستخدم الذي سجّل هذا المزوّد" + }, + organization_id: { + label: "المؤسسة", + help: "نطاق المؤسسة (عند استخدام SSO مقيَّد بنطاق المؤسسة)" + }, + created_at: { + label: "تاريخ الإنشاء" + }, + updated_at: { + label: "تاريخ التحديث" + } + }, + _views: { + all: { + label: "الكل", + emptyState: { + title: "لا يوجد مزوّدو SSO بعد", + message: "سجّل مزوّد الهوية الخارجي لمؤسستك — OIDC (Okta، Entra، Auth0، …) عبر «تسجيل مزوّد SSO»، أو SAML 2.0 عبر «تسجيل مزوّد SAML». بعدها يستطيع الأعضاء الذين يطابق نطاق بريدهم الإلكتروني تسجيل الدخول من خلاله." + } + } + }, + _actions: { + register_sso_provider: { + label: "تسجيل مزوّد SSO", + params: { + providerId: { + label: "معرّف المزوّد", + helpText: "معرّف ثابت، مثل \"okta\" أو \"acme-entra\"." + }, + issuer: { + label: "رابط المُصدِر", + helpText: "مُصدِر مزوّد الهوية، مثل https://acme.okta.com. يُجلب مستند الاكتشاف من هنا ما لم يُحدَّد رابط صريح أدناه." + }, + domain: { + label: "نطاق البريد الإلكتروني", + helpText: "يُوجَّه المستخدمون أصحاب نطاق البريد الإلكتروني هذا إلى مزوّد الهوية هذا، مثل acme.com." + }, + clientId: { + label: "معرّف العميل", + helpText: "معرّف عميل OAuth الذي أصدره مزوّد الهوية لهذا النظام." + }, + clientSecret: { + label: "سر العميل", + helpText: "سر عميل OAuth (يخزّنه better-auth مُشفَّرًا)." + }, + discoveryEndpoint: { + label: "رابط الاكتشاف", + helpText: "اختياري. رابط مستند اكتشاف OIDC. اتركه فارغًا لاشتقاق `/.well-known/openid-configuration`." + }, + scopes: { + label: "النطاقات", + helpText: "اختياري. نطاقات OAuth مفصولة بمسافات أو فواصل. القيمة الافتراضية \"openid email profile\".", + placeholder: "openid email profile" + }, + mapEmail: { + label: "التعيين: مطالبة البريد الإلكتروني", + helpText: "اختياري. المطالبة المعيَّنة إلى البريد الإلكتروني. القيمة الافتراضية \"email\".", + placeholder: "email" + }, + mapName: { + label: "التعيين: مطالبة الاسم", + helpText: "اختياري. المطالبة المعيَّنة إلى اسم العرض. القيمة الافتراضية \"name\".", + placeholder: "name" + } + } + }, + register_saml_provider: { + label: "تسجيل مزوّد SAML", + params: { + providerId: { + label: "معرّف المزوّد", + helpText: "معرّف ثابت، مثل \"acme-saml\"." + }, + issuer: { + label: "معرّف كيان مزوّد الهوية", + helpText: "معرّف EntityID في SAML لمزوّد الهوية (المُصدِر)، مثل https://saml.acme.com/entityid." + }, + domain: { + label: "نطاق البريد الإلكتروني", + helpText: "يُوجَّه المستخدمون أصحاب نطاق البريد الإلكتروني هذا إلى مزوّد الهوية هذا، مثل acme.com." + }, + entryPoint: { + label: "رابط SSO لمزوّد الهوية", + helpText: "نقطة نهاية تسجيل الدخول الموحَّد (إعادة التوجيه) في SAML لدى مزوّد الهوية التي تستقبل SAMLRequest." + }, + cert: { + label: "شهادة التوقيع لمزوّد الهوية", + helpText: "شهادة التوقيع X.509 لمزوّد الهوية (متن PEM). تُستخدم للتحقق من تواقيع التأكيدات." + }, + identifierFormat: { + label: "تنسيق NameID", + helpText: "اختياري. تنسيق NameID المطلوب في SAML. القيمة الافتراضية هي التنسيق المُهيَّأ لدى مزوّد الهوية.", + placeholder: "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" + } + } + }, + request_domain_verification: { + label: "طلب التحقق من النطاق", + resultDialog: { + title: "تحقّق من نطاقك", + description: "أضف سجل DNS من نوع TXT أدناه لدى مزوّد DNS لنطاقك، ثم شغّل «التحقق من النطاق». يُعرض الرمز مرة واحدة.", + acknowledge: "تم", + fields: { + dnsRecordType: "نوع السجل", + dnsRecordName: "الاسم / المضيف", + dnsRecordValue: "القيمة" + } + } + }, + verify_domain: { + label: "التحقق من النطاق", + successMessage: "تم التحقق من ملكية النطاق" + }, + delete_sso_provider: { + label: "حذف مزوّد SSO", + description: "هل تريد حذف مزوّد SSO هذا؟ لن يتمكن مستخدمو نطاقه من تسجيل الدخول من خلاله بعد ذلك.", + successMessage: "تم حذف مزوّد SSO" + } + } + }, + sys_scim_connection_binding: { + label: "ربط اتصال SCIM", + pluralLabel: "روابط اتصالات SCIM", + description: "حالة دورة الحياة لاتصال توفير SCIM، بما في ذلك آلة حالات إيقاف التشغيل الخاصة به", + fields: { + id: { + label: "المعرّف" + }, + connection_id: { + label: "معرّف الاتصال", + help: "معرّف اتصال SCIM غير القابل للتغيير الذي يحدد نطاق كل مورد مُوفَّر" + }, + connection_key: { + label: "مفتاح الاتصال", + help: "مفتاح تفرّد اتصال مشتق يتولى @better-auth/scim صيانته؛ لا تكتب فيه مباشرة." + }, + provisioning_domain_id: { + label: "نطاق التوفير", + help: "حدّ يملكه التطبيق ويستقبل الموارد المُوفَّرة (القيمة الافتراضية هي معرّف الاتصال)" + }, + decommission_status: { + label: "حالة إيقاف التشغيل", + help: "حالة دورة حياة الاتصال: active | reconciling | complete" + }, + decommissioned_at: { + label: "تاريخ إيقاف التشغيل" + }, + decommission_cursor_user_id: { + label: "مستخدم مؤشر إيقاف التشغيل", + help: "مؤشر تسوية قابل للاستئناف تتولى المكتبة صيانته" + }, + decommission_reconciled_user_count: { + label: "المستخدمون الذين تمت تسويتهم" + }, + decommission_batch_count: { + label: "دفعات إيقاف التشغيل" + }, + decommission_revision: { + label: "مراجعة إيقاف التشغيل", + help: "مراجعة التزامن التفاؤلي لآلة حالات إيقاف التشغيل" + }, + decommission_completed_at: { + label: "تاريخ اكتمال إيقاف التشغيل" + }, + decommission_lease_id: { + label: "حجز إيقاف التشغيل", + help: "حجز تسوية لعامل واحد تتولى المكتبة صيانته" + }, + decommission_lease_expires_at: { + label: "انتهاء حجز إيقاف التشغيل" + }, + created_at: { + label: "تاريخ الإنشاء" + } + }, + _views: { + all: { + label: "الكل" + } + } + }, + sys_scim_connection_credential: { + label: "بيانات اعتماد اتصال SCIM", + pluralLabel: "بيانات اعتماد اتصالات SCIM", + description: "بيانات اعتماد الحامل (ملخّصات أحادية الاتجاه) التي تصادق اتصالات توفير SCIM", + fields: { + id: { + label: "المعرّف" + }, + connection_id: { + label: "معرّف الاتصال", + help: "اتصال SCIM الذي تصادقه بيانات الاعتماد هذه (مثل \"okta-prod\")؛ يحدد نطاق كل مورد يوفّره مزوّد الهوية بها" + }, + provisioning_domain_id: { + label: "نطاق التوفير", + help: "حدّ يملكه التطبيق ويستقبل الموارد المُوفَّرة؛ القيمة الافتراضية هي معرّف الاتصال عند غيابه" + }, + organization_id: { + label: "المؤسسة", + help: "نطاق المؤسسة لهذا الاتصال، عندما يكون التوفير مقيَّدًا بنطاق المؤسسة" + }, + label: { + label: "التسمية", + help: "اسم موجَّه للمشغّل لبيانات الاعتماد هذه (مثل \"rotation 2026-Q3\")" + }, + token_digest: { + label: "ملخّص الرمز", + help: "قيمة HMAC-SHA-256 (base64url) لرمز الحامل، مفتاحها سر المصادقة للنشر — أحادية الاتجاه؛ يُعرض النص الصريح مرة واحدة عند الإصدار ولا يُخزَّن أبدًا." + }, + active: { + label: "نشط", + help: "مفتاح الإبطال — تُرفض بيانات الاعتماد غير النشطة عند التحقق" + }, + expires_at: { + label: "تاريخ الانتهاء", + help: "انتهاء صلاحية صارم اختياري لتدوير بيانات الاعتماد على مراحل؛ تُرفض بيانات الاعتماد المنتهية الصلاحية" + }, + user_id: { + label: "أصدرها", + help: "المستخدم الذي أصدر بيانات الاعتماد هذه" + }, + created_at: { + label: "تاريخ الإنشاء" + }, + updated_at: { + label: "تاريخ التحديث" + } + }, + _views: { + all: { + label: "الكل" + } + }, + _actions: { + mint_scim_credential: { + label: "إصدار بيانات اعتماد", + description: "إصدار بيانات اعتماد حامل لاتصال SCIM. سلّمها إلى مزوّد الهوية بوصفها رمز SCIM؛ وتُعرض مرة واحدة.", + params: { + connectionId: { + label: "معرّف الاتصال", + helpText: "اتصال SCIM الذي تصادقه بيانات الاعتماد هذه، مثل \"okta-prod\". 246 حرفًا على الأكثر. للتدوير، أصدر بيانات اعتماد ثانية للاتصال نفسه، وانقل مزوّد الهوية إليها، ثم ألغِ القديمة." + }, + label: { + label: "التسمية", + helpText: "اختياري. اسم لبيانات الاعتماد هذه، مثل \"تدوير 2026-Q3\"." + }, + organizationId: { + label: "المؤسسة", + helpText: "اختياري. المؤسسة التي تملك الاتصال. اتركه فارغًا للمؤسسة الافتراضية." + }, + provisioningDomainId: { + label: "نطاق التوفير", + helpText: "اختياري. الحدّ الذي يستقبل المستخدمين والمجموعات المُوفَّرين. اتركه فارغًا لاستخدام معرّف الاتصال." + }, + expiresAt: { + label: "تاريخ الانتهاء", + helpText: "اختياري. بعد هذا الوقت تُرفض بيانات الاعتماد. اتركه فارغًا لبيانات اعتماد لا تنتهي صلاحيتها." + } + }, + resultDialog: { + title: "صدرت بيانات اعتماد SCIM", + description: "انسخ الرمز الآن — فهو لا يُعرض إلا مرة واحدة ولا يمكن استرداده. الصقه في مزوّد الهوية بوصفه رمز حامل SCIM. إذا تسرّب، فألغِه وأصدر رمزًا جديدًا.", + acknowledge: "لقد حفظت الرمز", + fields: { + token: "الرمز", + connectionId: "معرّف الاتصال", + expiresAt: "تاريخ الانتهاء" + } + } + }, + revoke_scim_credential: { + label: "إلغاء بيانات الاعتماد", + confirmText: "هل تريد إلغاء بيانات اعتماد SCIM هذه؟ يُرفض مزوّد الهوية الذي يستخدمها بدءًا من طلبه التالي. لا يمكن التراجع عن ذلك: لاستعادة التوفير، أصدر بيانات اعتماد جديدة.", + successMessage: "أُلغيت بيانات اعتماد SCIM" + } + } + }, + sys_scim_group: { + label: "مجموعة SCIM", + pluralLabel: "مجموعات SCIM", + description: "مجموعات SCIM 2.0 التي يدفعها مزوّد الهوية، مقيَّدة بنطاق كل اتصال توفير", + fields: { + id: { + label: "المعرّف" + }, + connection_id: { + label: "معرّف الاتصال" + }, + provisioning_domain_id: { + label: "نطاق التوفير" + }, + revision: { + label: "المراجعة", + help: "مراجعة التزامن التفاؤلي التي يتولى @better-auth/scim صيانتها" + }, + display_name: { + label: "اسم العرض", + help: "قيمة displayName في SCIM كما يرسلها مزوّد الهوية" + }, + display_name_key: { + label: "مفتاح اسم العرض", + help: "مفتاح تفرّد مشتق لـ displayName بعد توحيد حالة الأحرف يتولى @better-auth/scim صيانته؛ لا تكتب فيه مباشرة." + }, + external_id: { + label: "المعرّف الخارجي", + help: "قيمة externalId التي يُسندها مزوّد الهوية، عندما يرسلها" + }, + external_id_key: { + label: "مفتاح المعرّف الخارجي", + help: "مفتاح تفرّد مشتق لـ externalId يتولى @better-auth/scim صيانته؛ لا تكتب فيه مباشرة." + }, + order_key: { + label: "مفتاح الترتيب", + help: "مفتاح ترقيم صفحات ثابت مشتق يتولى @better-auth/scim صيانته؛ لا تكتب فيه مباشرة." + }, + created_at: { + label: "تاريخ الإنشاء" + }, + updated_at: { + label: "تاريخ التحديث" + } + }, + _views: { + all: { + label: "الكل" + } + } + }, + sys_scim_group_member: { + label: "عضو مجموعة SCIM", + pluralLabel: "أعضاء مجموعات SCIM", + description: "صفوف العضوية في مجموعات SCIM التي يدفعها مزوّد الهوية (مجموعة ↔ مستخدم مُوفَّر)", + fields: { + id: { + label: "المعرّف" + }, + display_title: { + label: "العنوان", + help: "عنوان السجل: المستخدم المُوفَّر والمجموعة (يُحسب عند القراءة)" + }, + connection_id: { + label: "معرّف الاتصال" + }, + group_id: { + label: "المجموعة" + }, + scim_user_id: { + label: "مستخدم SCIM" + }, + membership_key: { + label: "مفتاح العضوية", + help: "مفتاح تفرّد عضوية مشتق يتولى @better-auth/scim صيانته؛ لا تكتب فيه مباشرة." + }, + created_at: { + label: "تاريخ الإنشاء" + } + }, + _views: { + all: { + label: "الكل" + } + } + }, + sys_scim_identity_tombstone: { + label: "شاهد هوية SCIM المحذوفة", + pluralLabel: "شواهد هويات SCIM المحذوفة", + description: "شواهد هويات SCIM التي حذفها مزوّد الهوية، تُحفظ ليُتعرَّف على إعادة التوفير", + fields: { + id: { + label: "المعرّف" + }, + connection_id: { + label: "معرّف الاتصال" + }, + provisioning_domain_id: { + label: "نطاق التوفير" + }, + external_id: { + label: "المعرّف الخارجي", + help: "قيمة externalId التي أسندها مزوّد الهوية للهوية المحذوفة" + }, + external_id_key: { + label: "مفتاح المعرّف الخارجي", + help: "مفتاح تفرّد مشتق لـ externalId يتولى @better-auth/scim صيانته؛ لا تكتب فيه مباشرة." + }, + user_id: { + label: "المستخدم", + help: "مستخدم المنصة الذي كانت الهوية المحذوفة مرتبطة به" + }, + profile: { + label: "الملف الشخصي النهائي", + help: "ملف SCIM الشخصي النهائي المُسلسَل وقت الحذف، يتولى @better-auth/scim صيانته" + }, + deleted_at: { + label: "تاريخ الحذف" + } + }, + _views: { + all: { + label: "الكل" + } + } + }, + sys_scim_projection_grant: { + label: "منح إسقاط SCIM", + pluralLabel: "منح إسقاط SCIM", + description: "منح الأدوار/الاستحقاقات التي يُسقطها توفير SCIM على مستخدمي المنصة", + fields: { + id: { + label: "المعرّف" + }, + display_title: { + label: "العنوان", + help: "عنوان السجل: الدور المُسقَط والمستخدم الممنوح له (يُحسب عند القراءة)" + }, + connection_id: { + label: "معرّف الاتصال" + }, + provisioning_domain_id: { + label: "نطاق التوفير" + }, + scim_user_id: { + label: "مستخدم SCIM", + help: "إسقاط المستخدم الخاص بكل اتصال الذي يتبع له هذا المنح" + }, + user_id: { + label: "المستخدم", + help: "مستخدم المنصة الذي يُسقَط عليه المنح" + }, + source_kind: { + label: "نوع المصدر", + help: "ما الذي أسقط المنح (مثل group، entitlement، attribute)" + }, + source_id: { + label: "معرّف المصدر", + help: "معرّف المصدر المُسقِط (مثل معرّف مجموعة SCIM)" + }, + source_value: { + label: "قيمة المصدر", + help: "قيمة سمة المصدر، عندما يحمل نوع المصدر قيمة" + }, + role: { + label: "الدور", + help: "الدور/الاستحقاق المُسقَط على المستخدم" + }, + grant_key: { + label: "مفتاح المنح", + help: "مفتاح تفرّد منح مشتق يتولى @better-auth/scim صيانته؛ لا تكتب فيه مباشرة." + }, + created_at: { + label: "تاريخ الإنشاء" + }, + updated_at: { + label: "تاريخ التحديث" + } + }, + _views: { + all: { + label: "الكل" + } + } + }, + sys_scim_subject: { + label: "موضوع SCIM", + pluralLabel: "مواضيع SCIM", + description: "رابط توفير SCIM لكل مستخدم — صف واحد لكل مستخدم يوفّره أي اتصال SCIM", + fields: { + id: { + label: "المعرّف" + }, + user_id: { + label: "المستخدم", + help: "مستخدم المنصة الذي يتتبعه صف الموضوع هذا (فريد — صف واحد لكل مستخدم)" + }, + profile_source_id: { + label: "مصدر الملف الشخصي", + help: "إسقاط sys_scim_user الذي يغذّي حاليًا الملف الشخصي لهذا المستخدم" + }, + revision: { + label: "المراجعة", + help: "مراجعة التزامن التفاؤلي التي يتولى @better-auth/scim صيانتها" + }, + created_at: { + label: "تاريخ الإنشاء" + }, + updated_at: { + label: "تاريخ التحديث" + } + }, + _views: { + all: { + label: "الكل" + } + } + }, + sys_scim_user: { + label: "مستخدم SCIM", + pluralLabel: "مستخدمو SCIM", + description: "إسقاط خاص بكل اتصال لمستخدم وفّره مزوّد الهوية (SCIM 2.0 /Users)", + fields: { + id: { + label: "المعرّف" + }, + connection_id: { + label: "معرّف الاتصال", + help: "اتصال SCIM الذي وفّر هذا المستخدم" + }, + provisioning_domain_id: { + label: "نطاق التوفير" + }, + user_id: { + label: "المستخدم", + help: "مستخدم المنصة الذي تجسّد فيه هذا الإسقاط" + }, + connection_user_key: { + label: "مفتاح مستخدم الاتصال", + help: "مفتاح تفرّد مشتق للزوج (الاتصال، المستخدم) يتولى @better-auth/scim صيانته؛ لا تكتب فيه مباشرة." + }, + user_name: { + label: "اسم المستخدم", + help: "قيمة userName في SCIM كما يرسلها مزوّد الهوية" + }, + user_name_key: { + label: "مفتاح اسم المستخدم", + help: "مفتاح تفرّد مشتق لـ userName بعد توحيد حالة الأحرف يتولى @better-auth/scim صيانته؛ لا تكتب فيه مباشرة." + }, + primary_email: { + label: "البريد الإلكتروني الأساسي" + }, + work_email_value_index: { + label: "فهرس بريد العمل", + help: "فهرس بحث مشتق يتولى @better-auth/scim صيانته؛ لا تكتب فيه مباشرة." + }, + email_value_index: { + label: "فهرس البريد الإلكتروني", + help: "فهرس بحث مشتق يتولى @better-auth/scim صيانته؛ لا تكتب فيه مباشرة." + }, + display_name: { + label: "اسم العرض" + }, + formatted_name: { + label: "الاسم المنسَّق" + }, + given_name: { + label: "الاسم الأول" + }, + family_name: { + label: "اسم العائلة" + }, + serialized_emails: { + label: "عناوين البريد الإلكتروني (مُسلسَلة)", + help: "قائمة عناوين البريد الإلكتروني في SCIM مُسلسَلة بالشكل المعتمد، يتولى @better-auth/scim صيانتها؛ لا تكتب فيها مباشرة." + }, + serialized_attributes: { + label: "السمات (مُسلسَلة)", + help: "سمات SCIM مُسلسَلة بالشكل المعتمد، يتولى @better-auth/scim صيانتها؛ لا تكتب فيها مباشرة." + }, + external_id: { + label: "المعرّف الخارجي", + help: "قيمة externalId التي يُسندها مزوّد الهوية، عندما يرسلها" + }, + external_id_key: { + label: "مفتاح المعرّف الخارجي", + help: "مفتاح تفرّد مشتق لـ externalId يتولى @better-auth/scim صيانته؛ لا تكتب فيه مباشرة." + }, + active: { + label: "نشط", + help: "علامة active في SCIM — القيمة false تعني أن مزوّد الهوية عطّل هذا المستخدم" + }, + order_key: { + label: "مفتاح الترتيب", + help: "مفتاح ترقيم صفحات ثابت مشتق يتولى @better-auth/scim صيانته؛ لا تكتب فيه مباشرة." + }, + created_at: { + label: "تاريخ الإنشاء" + }, + updated_at: { + label: "تاريخ التحديث" + } + }, + _views: { + all: { + label: "الكل" + } + } + }, + sys_notification: { + label: "حدث الإشعار", + pluralLabel: "أحداث الإشعارات", + description: "كل إشعار أرسلته المنصة، صف واحد لكل حدث.", + fields: { + id: { + label: "معرّف الإشعار" + }, + topic: { + label: "الموضوع", + help: "موضوع الإشعار، مثل task.assigned، collab.mention" + }, + payload: { + label: "الحمولة", + help: "مدخلات القالب المنقولة إلى القنوات (title/body/url/actor/source/…)" + }, + severity: { + label: "درجة الخطورة", + help: "تلميح درجة الخطورة للعرض / التصفية", + options: { + info: "معلومات", + warning: "تحذير", + critical: "حرج" + } + }, + suppressed_channels: { + label: "القنوات المُستبعدة", + help: "القنوات التي تخطّاها التوزيع لأنها غير متاحة لهذا المستأجر، بالشكل [{channel, reason}]؛ والسبب من مجموعة مغلقة: transport_not_configured" + }, + dedup_key: { + label: "مفتاح إزالة التكرار", + help: "مفتاح عدم التكرار ضمن نافذة الموضوع؛ الإطلاق المكرر لا يفعل شيئًا" + }, + source_object: { + label: "الكائن المصدر", + help: "اسم كائن السجل المرتبط (مثل lead، opportunity)" + }, + source_id: { + label: "السجل المصدر", + help: "معرّف السجل داخل source_object" + }, + actor_id: { + label: "المنفِّذ", + help: "المستخدم الذي تسبب في الحدث (صاحب الإشارة، صاحب الإسناد)" + }, + created_at: { + label: "تاريخ الإنشاء" + } + }, + _views: { + recent: { + label: "الأحدث", + emptyState: { + title: "لا توجد أحداث", + message: "لم يُطلَق أي حدث إشعار." + } + }, + by_topic: { + label: "حسب الموضوع" + } + } + }, + sys_attachment: { + label: "المرفق", + pluralLabel: "المرفقات", + description: "ربط متعدد الأشكال بين sys_file وأي سجل آخر", + fields: { + id: { + label: "معرّف المرفق" + }, + parent_object: { + label: "الكائن الأصل", + help: "الاسم المختصر لكائن السجل المُرفَق به (مثل account، lead)" + }, + parent_id: { + label: "السجل الأصل", + help: "المفتاح الأساسي للسجل المُرفَق به" + }, + file_id: { + label: "الملف", + help: "إدخال التخزين sys_file الذي يُرفَق" + }, + file_name: { + label: "اسم الملف", + help: "نسخة غير مُطبَّعة من sys_file.name لعرض القوائم بسرعة" + }, + mime_type: { + label: "نوع MIME" + }, + size: { + label: "الحجم (بايت)" + }, + uploaded_by: { + label: "رفعه" + }, + description: { + label: "الوصف" + }, + created_at: { + label: "تاريخ الإنشاء" + }, + updated_at: { + label: "تاريخ التحديث" + } + } + }, + sys_email: { + label: "البريد الإلكتروني", + pluralLabel: "رسائل البريد الإلكتروني", + description: "سجل تسليم البريد الإلكتروني الصادر", + fields: { + id: { + label: "معرّف البريد الإلكتروني" + }, + message_id: { + label: "Message-ID", + help: "قيمة Message-ID وفق RFC-5322 التي يُسندها الناقل" + }, + from_address: { + label: "المرسِل" + }, + to_addresses: { + label: "إلى", + help: "عناوين المستلمين مفصولة بفواصل" + }, + cc_addresses: { + label: "نسخة إلى" + }, + bcc_addresses: { + label: "نسخة مخفية إلى" + }, + reply_to: { + label: "الرد إلى" + }, + subject: { + label: "الموضوع" + }, + body_text: { + label: "النص (نص عادي)" + }, + body_html: { + label: "النص (HTML)" + }, + headers_json: { + label: "الترويسات (JSON)", + help: "الترويسات المخصّصة المرفقة بالرسالة عند إرسالها، ككائن JSON من الاسم ← القيمة. تُكتب في وضعَي التسليم كليهما (فهي دليل تدقيق بقدر ما هي مدخل للتسليم). غائبة في الصفوف المكتوبة قبل وجود هذا العمود، وتُقرأ على أنها \"لا ترويسات مخصّصة\". لا تُعاد أبدًا على مسار البيانات العام — فالترويسات هي المكان المعتاد لوضع بيانات الاعتماد؛ وتستردّها مسارات التسليم عبر أداة الوصول المميَّزة في المحرك." + }, + attachments_json: { + label: "المرفقات (JSON)", + help: "المرفقات كمصفوفة JSON من { filename, contentType?, size, hash, cid?, contentForm, inline?, storageKey?, contentReclaimedAt? }. المحتوى حتى ميزانية plugin-email (SYS_EMAIL_ATTACHMENT_LIMIT_BYTES، ‏256 KiB خامًا مجتمعة — نحو 350 KB بترميز base64 في أسوأ الأحوال) يُخزَّن بترميز base64 في `inline`؛ أما المحتوى الأكبر فيذهب إلى قدرة تخزين الملفات ويحمل العنصر `storageKey` بدلًا منه، فيبقى حجم الصف محدودًا في الحالتين. القيم filename/contentType/size/hash دليل تدقيق دائم؛ أما المحتوى خارج الصف فهو أثر من آثار التسليم ويُحذف بعد مهلة سماح من وصول الصف إلى حالة نهائية، وعندها يُستبدل `storageKey` بـ `contentReclaimedAt`." + }, + status: { + label: "الحالة", + help: "حالة دورة الحياة — queued عند تقديم الرسالة للإرسال، قبل أول محاولة تسليم", + options: { + queued: "في قائمة الانتظار", + sent: "مُرسَل", + failed: "فشل" + } + }, + error: { + label: "الخطأ", + help: "سبب فشل الرسالة — خطأ في الناقل، أو التحقق الذي رفضها قبل التسليم." + }, + attempt_count: { + label: "المحاولات", + help: "عدد محاولات التسليم التي أجرتها الخدمة" + }, + sent_at: { + label: "وقت الإرسال", + help: "يُعيَّن عند انتقال الحالة إلى \"sent\"" + }, + related_object: { + label: "الكائن المرتبط", + help: "اسم كائن السجل المرتبط (مثل lead، opportunity)" + }, + related_id: { + label: "السجل المرتبط", + help: "معرّف السجل داخل related_object" + }, + sent_by: { + label: "أرسله" + }, + created_at: { + label: "تاريخ الإنشاء" + }, + updated_at: { + label: "تاريخ التحديث" + } + } + }, + sys_email_template: { + label: "قالب البريد الإلكتروني", + pluralLabel: "قوالب البريد الإلكتروني", + description: "قالب بريد إلكتروني صادر (الموضوع + النص + المتغيرات) يُحَلّ حسب الاسم + اللغة", + fields: { + id: { + label: "المعرّف" + }, + name: { + label: "الاسم", + help: "معرّف بصيغة snake_case مفصول بنقاط (مثل auth.password_reset)" + }, + label: { + label: "التسمية", + help: "اسم مقروء يظهر في Studio" + }, + category: { + label: "الفئة", + options: { + auth: "المصادقة", + notification: "الإشعارات", + workflow: "سير العمل", + marketing: "التسويق", + custom: "مخصّص" + } + }, + locale: { + label: "اللغة", + help: "وسم لغة بصيغة BCP-47 (en-US، zh-CN، …)" + }, + description: { + label: "الوصف" + }, + subject: { + label: "الموضوع", + help: "قالب الموضوع؛ يدعم العناصر النائبة {{var.path}}" + }, + body_html: { + label: "نص HTML", + help: "قالب نص HTML؛ يدعم العناصر النائبة {{var.path}}" + }, + body_text: { + label: "النص العادي", + help: "بديل اختياري بنص عادي (يُشتق تلقائيًا من HTML عند تركه فارغًا)" + }, + from_name: { + label: "اسم المرسِل" + }, + from_address: { + label: "عنوان المرسِل" + }, + reply_to: { + label: "الرد إلى" + }, + active: { + label: "نشط" + }, + is_system: { + label: "قالب النظام", + help: "مقدَّم من إضافة / من المنصة" + }, + variables_json: { + label: "المتغيرات (JSON)", + help: "مصفوفة JSON من {name,type,required,description}" + }, + managed_by: { + label: "جهة الإدارة", + help: "مصدر السجل: platform = مدمج في إطار العمل / package = مُعلَن من تطبيق أو حزمة (يُزرع عند الإقلاع من البيانات الوصفية email_template المُعلَنة) / admin = أُنشئ في Studio.", + options: { + platform: "المنصّة", + package: "الحزمة", + admin: "المسؤول" + } + }, + customized: { + label: "مخصَّص", + help: "يُعيَّن على قالب مُعلَن من حزمة عدّله مسؤول قبل إغلاق تعديل القوالب على مستوى المؤسسة؛ لا يستبدل الزرع عند الإقلاع مثل هذا الصف أبدًا. لم يعد شيء يعيّنه. لا معنى له في صفوف admin." + }, + created_at: { + label: "تاريخ الإنشاء" + }, + updated_at: { + label: "تاريخ التحديث" + } + } + }, + sys_job: { + label: "مهمة خلفية", + pluralLabel: "المهام الخلفية", + description: "فهرس المهام الخلفية المسجَّلة", + fields: { + id: { + label: "معرّف المهمة" + }, + name: { + label: "اسم المهمة", + help: "معرّف مهمة فريد (snake_case)، فريد على مستوى التثبيت بأكمله — ففهرس المهام خاصية للنشر، لا لمؤسسة" + }, + schedule_type: { + label: "نوع الجدولة", + options: { + cron: "cron", + interval: "فاصل زمني", + once: "مرة واحدة" + } + }, + schedule_expression: { + label: "التعبير", + help: "تعبير cron / فاصل زمني بالمللي ثانية / تاريخ ووقت بصيغة ISO" + }, + timezone: { + label: "المنطقة الزمنية" + }, + active: { + label: "نشط", + help: "ما إذا كان المُجدوِل يشغّل هذه المهمة حاليًا" + }, + last_run_at: { + label: "آخر تشغيل" + }, + last_status: { + label: "آخر حالة", + options: { + success: "نجاح", + failed: "فشل", + timeout: "انتهاء المهلة", + running: "قيد التشغيل", + degraded: "متدهور" + } + }, + last_error: { + label: "آخر خطأ" + }, + run_count: { + label: "عدد مرات التشغيل" + }, + failure_count: { + label: "عدد مرات الفشل" + }, + created_at: { + label: "تاريخ الإنشاء" + }, + updated_at: { + label: "تاريخ التحديث" + } + } + }, + sys_job_run: { + label: "تشغيل المهمة", + pluralLabel: "عمليات تشغيل المهام", + description: "سجل تنفيذ المهام الخلفية", + fields: { + id: { + label: "معرّف التشغيل" + }, + job_name: { + label: "المهمة" + }, + status: { + label: "الحالة", + options: { + running: "قيد التشغيل", + success: "نجاح", + failed: "فشل", + timeout: "انتهاء المهلة", + degraded: "متدهور" + } + }, + started_at: { + label: "وقت البدء" + }, + completed_at: { + label: "تاريخ الإكمال" + }, + duration_ms: { + label: "المدة (مللي ثانية)" + }, + attempt: { + label: "المحاولة", + help: "1 للتشغيل الأول، و>1 لإعادة المحاولات/إعادة التشغيل" + }, + trigger: { + label: "المشغِّل", + options: { + schedule: "الجدول الزمني", + manual: "يدوي", + replay: "إعادة تشغيل" + } + }, + error: { + label: "الخطأ" + }, + created_at: { + label: "تاريخ الإنشاء" + } + } + }, + sys_job_queue: { + label: "رسالة قائمة انتظار المهام", + pluralLabel: "رسائل قائمة انتظار المهام", + description: "قائمة انتظار دائمة للمهام/الرسائل، بما فيها الرسائل الميتة", + fields: { + id: { + label: "معرّف الرسالة" + }, + queue: { + label: "قائمة الانتظار", + help: "اسم قائمة الانتظار المنطقي (snake_case)" + }, + idempotency_key: { + label: "مفتاح عدم التكرار", + help: "مفتاح إزالة التكرار ضمن (قائمة الانتظار، النافذة الزمنية)" + }, + payload_json: { + label: "الحمولة (JSON)", + help: "متن الرسالة المُسلسَل" + }, + metadata_json: { + label: "البيانات الوصفية (JSON)", + help: "حقيبة بيانات وصفية مُسلسَلة (tenant_id، source_record، ...)" + }, + status: { + label: "الحالة", + help: "حالة دورة الحياة", + options: { + pending: "قيد الانتظار", + running: "قيد التشغيل", + completed: "مكتمل", + failed: "فشل", + dlq: "قائمة الرسائل الميتة (DLQ)" + } + }, + priority: { + label: "الأولوية", + help: "الأقل = أولوية أعلى" + }, + attempts: { + label: "المحاولات" + }, + max_attempts: { + label: "الحد الأقصى للمحاولات" + }, + backoff_type: { + label: "التراجع", + options: { + fixed: "ثابت", + exponential: "أُسّي" + } + }, + backoff_delay_ms: { + label: "أساس التراجع (مللي ثانية)" + }, + backoff_max_delay_ms: { + label: "سقف التراجع (مللي ثانية)" + }, + scheduled_for: { + label: "مجدول في", + help: "أبكر وقت يجوز فيه لعامل حجز هذه الرسالة" + }, + locked_by: { + label: "مقفل بواسطة", + help: "معرّف العامل الذي يحمل الحجز" + }, + locked_until: { + label: "مقفل حتى", + help: "انتهاء مهلة الحجز؛ إذا انقضت، يجوز لعامل آخر المطالبة بالرسالة" + }, + last_error: { + label: "آخر خطأ" + }, + completed_at: { + label: "تاريخ الإكمال" + }, + created_at: { + label: "تاريخ الإنشاء" + }, + updated_at: { + label: "تاريخ التحديث" + } + } + }, + sys_import_job: { + label: "مهمة استيراد", + pluralLabel: "مهام الاستيراد", + description: "حالة مهام الاستيراد الجماعي غير المتزامنة وتقدّمها وسجلها", + fields: { + id: { + label: "معرّف المهمة" + }, + object_name: { + label: "الكائن", + help: "اسم API للكائن الذي يُستورد إليه" + }, + status: { + label: "الحالة", + options: { + pending: "قيد الانتظار", + running: "قيد التشغيل", + succeeded: "نجحت", + failed: "فشل", + cancelled: "أُلغيت" + } + }, + total_rows: { + label: "إجمالي الصفوف" + }, + processed_rows: { + label: "الصفوف المعالَجة" + }, + created_count: { + label: "تم الإنشاء" + }, + updated_count: { + label: "تم التحديث" + }, + skipped_count: { + label: "تم التخطي" + }, + error_count: { + label: "الأخطاء" + }, + write_mode: { + label: "وضع الكتابة", + options: { + insert: "إدراج", + update: "تحديث", + upsert: "إدراج أو تحديث" + } + }, + dry_run: { + label: "تشغيل تجريبي" + }, + run_automations: { + label: "تشغيل الأتمتة" + }, + treat_as_historical: { + label: "المعاملة كبيانات تاريخية" + }, + error: { + label: "سبب الفشل" + }, + results: { + label: "نتائج الصفوف (عينة)", + help: "عينة محدودة من نتائج كل صف (الإخفاقات أولًا) لواجهة المستخدم" + }, + undo_log: { + label: "سجل التراجع", + help: "تعليمات العكس ({created:[ids], updated:[{id,before}]}) تُلتقط لمهام الاستيراد الصغيرة غير التجريبية حتى يمكن التراجع عن الاستيراد. التراجع الجزئي يُبقي هنا الصفوف التي لم تُعكس فقط، مع قائمة إخفاقات تذكر لكل صف معرّفه والإجراء والرمز وسبب الرفض" + }, + reverted_at: { + label: "تاريخ التراجع", + help: "يُعيَّن عند التراجع عن الاستيراد (حُذفت السجلات المُنشأة، واستُعيدت السجلات المُحدَّثة)" + }, + started_at: { + label: "وقت البدء" + }, + completed_at: { + label: "تاريخ الإكمال" + }, + created_by: { + label: "المُنشئ" + }, + created_at: { + label: "تاريخ الإنشاء" + } + } + }, + sys_metadata: { + label: "البيانات الوصفية للنظام", + pluralLabel: "البيانات الوصفية للنظام", + description: "يخزّن سجلات البيانات الوصفية على مستوى المنصة والمستخدم (الكائنات، طرق العرض، التدفقات، إلخ)", + fields: { + id: { + label: "المعرّف" + }, + name: { + label: "الاسم" + }, + type: { + label: "نوع البيانات الوصفية" + }, + namespace: { + label: "مساحة الاسم" + }, + package_id: { + label: "معرّف الحزمة", + help: "سلسلة معرّف بيان الحزمة القديمة. استخدم package_version_id للسجلات الجديدة." + }, + package_version_id: { + label: "إصدار الحزمة", + help: "إصدار الحزمة الذي ثُبِّتت منه هذه البيانات الوصفية. فارغ للبيانات الوصفية المدمجة في المنصة وللتغييرات التي أُجريت في هذه البيئة." + }, + managed_by: { + label: "جهة الإدارة", + options: { + package: "الحزمة", + platform: "المنصّة", + user: "المستخدم" + } + }, + scope: { + label: "النطاق", + options: { + system: "النظام", + platform: "المنصّة", + user: "المستخدم" + } + }, + bundle_member: { + label: "عضو الحزمة المجمَّعة", + help: "عضو الحزمة المجمَّعة (مثل لغة قالب بريد إلكتروني) الذي يخصّصه صف التراكب هذا. القيمة الفارغة = العضو المعتمد." + }, + metadata: { + label: "البيانات الوصفية", + help: "حمولة البيانات الوصفية بصيغة JSON مُسلسَلة" + }, + draft_changes: { + label: "تغييرات المسودة", + help: "مصدر كل مفتاح في مسودة معلّقة بصيغة JSON مُسلسَلة: أي واجهة (copilot، studio، api) وأي منفِّذ ومتى غُيِّر كل مسار مفتاح آخر مرة. فارغ في الصفوف النشطة." + }, + extends: { + label: "يمتد من" + }, + strategy: { + label: "الاستراتيجية", + options: { + merge: "دمج", + replace: "استبدال" + } + }, + owner: { + label: "المالك" + }, + state: { + label: "الحالة", + options: { + draft: "مسودة", + active: "نشط", + archived: "مؤرشف", + deprecated: "مُهمَل" + } + }, + environment_id: { + label: "البيئة (مُهمَل)", + help: "مُهمَل. التراكبات صفوف خاصة بالبيئة؛ لا يوجد عمود مستأجر." + }, + version: { + label: "الإصدار" + }, + checksum: { + label: "المجموع الاختباري" + }, + source: { + label: "المصدر", + options: { + filesystem: "نظام الملفات", + database: "قاعدة البيانات", + api: "API", + migration: "الترحيل" + } + }, + tags: { + label: "الوسوم", + help: "مصفوفة وسوم تصنيف بصيغة JSON مُسلسَلة" + }, + created_by: { + label: "المُنشئ" + }, + created_at: { + label: "تاريخ الإنشاء" + }, + updated_by: { + label: "حدّثه" + }, + updated_at: { + label: "تاريخ التحديث" + } + }, + _views: { + only_objects: { + label: "الكائنات" + }, + only_fields: { + label: "الحقول" + }, + all_metadata: { + label: "الكل" + } + } + }, + sys_metadata_history: { + label: "سجل البيانات الوصفية", + pluralLabel: "سجل البيانات الوصفية", + description: "سجل أحداث دائم لتغييرات تراكبات البيانات الوصفية (على مستوى البيئة، إلحاق فقط)", + fields: { + id: { + label: "المعرّف" + }, + event_seq: { + label: "تسلسل الحدث", + help: "مؤشر سجل أحداث متزايد رتيبًا لكل مؤسسة." + }, + name: { + label: "الاسم" + }, + type: { + label: "نوع البيانات الوصفية" + }, + version: { + label: "الإصدار" + }, + operation_type: { + label: "نوع العملية", + options: { + create: "إنشاء", + update: "تحديث", + publish: "نشر", + revert: "استرجاع", + delete: "حذف" + } + }, + metadata: { + label: "البيانات الوصفية", + help: "لقطة البيانات الوصفية في هذا الإصدار بصيغة JSON مُسلسَلة (فارغة في عمليات الحذف)." + }, + checksum: { + label: "المجموع الاختباري" + }, + previous_checksum: { + label: "المجموع الاختباري السابق" + }, + change_note: { + label: "ملاحظة التغيير", + help: "وصف لما تغيّر في هذا الإصدار." + }, + source: { + label: "المصدر" + }, + recorded_by: { + label: "سجّله", + help: "المستخدم الذي أجرى هذا التغيير. NULL = بمبادرة من النظام (مزامنة الإقلاع، الترحيل، مهمة مجدولة) — وليس أبدًا سلسلة نصية بديلة." + }, + recorded_at: { + label: "تاريخ التسجيل" + } + } + }, + sys_metadata_audit: { + label: "تدقيق البيانات الوصفية", + pluralLabel: "تدقيق البيانات الوصفية", + description: "سجل تدقيق إلحاقي فقط لقرارات الكتابة على البيانات الوصفية.", + fields: { + id: { + label: "المعرّف" + }, + occurred_at: { + label: "وقت الحدوث" + }, + actor: { + label: "المنفِّذ", + help: "الجهة الفاعلة — معرّف مستخدم، أو معرّف نظام، أو \"system\"." + }, + source: { + label: "المصدر" + }, + type: { + label: "نوع البيانات الوصفية" + }, + name: { + label: "الاسم" + }, + operation: { + label: "العملية", + options: { + save: "حفظ", + publish: "نشر", + rollback: "تراجع", + delete: "حذف", + reset: "إعادة تعيين" + } + }, + outcome: { + label: "النتيجة", + options: { + allowed: "مسموح", + denied: "مرفوض", + forced: "مفروض" + } + }, + code: { + label: "الرمز" + }, + lock_state: { + label: "حالة القفل" + }, + lock_overridden: { + label: "تم تجاوز القفل" + }, + request_id: { + label: "معرّف الطلب" + }, + note: { + label: "ملاحظة" + } + } + }, + sys_setting: { + label: "الإعداد", + pluralLabel: "الإعدادات", + description: "مخزن مفتاح/قيمة عام يدعم عقد SettingsManifest.", + fields: { + id: { + label: "معرّف الإعداد" + }, + created_at: { + label: "تاريخ الإنشاء" + }, + updated_at: { + label: "تاريخ التحديث" + }, + namespace: { + label: "مساحة الاسم", + help: "مساحة اسم البيان (مثل mail، branding، storage)." + }, + key: { + label: "المفتاح", + help: "مفتاح المحدِّد داخل مساحة الاسم (snake_case)." + }, + scope: { + label: "النطاق", + help: "الطبقة التي ينتمي إليها هذا الصف في تسلسل حلّ التهيئة.", + options: { + tenant: "المستأجر", + user: "المستخدم" + } + }, + user_id: { + label: "المستخدم", + help: "المستخدم المالك عندما يكون scope=user؛ وفارغ في غير ذلك." + }, + value: { + label: "القيمة", + help: "قيمة مُرمَّزة بصيغة JSON. فارغة عندما يكون encrypted=true (انظر value_enc)." + }, + encrypted: { + label: "مُشفَّر", + help: "عندما تكون القيمة true، تُخزَّن القيمة مُشفَّرة أثناء التخزين في value_enc؛ ويكون عمود value فارغًا." + }, + locked: { + label: "مقفل", + help: "عندما تكون القيمة true، لا يمكن لصفوف النطاقات الأدنى تجاوز هذه القيمة؛ وتُعيد عمليات الكتابة على النطاقات الأدنى 409. يثبّت صف المستأجر قيمته لمستخدمي ذلك المستأجر (تتالي المرحلة 2)." + }, + locked_reason: { + label: "سبب القفل", + help: "شرح مقروء يظهر في تلميح الواجهة عندما يكون locked=true." + }, + value_enc: { + label: "القيمة المُشفَّرة", + help: "حمولة النص المُشفَّر (مغلَّفة بـ KMS). تُعيَّن فقط عندما يكون encrypted=true." + }, + updated_by: { + label: "حدّثه", + help: "آخر جهة فاعلة كتبت هذا الصف عبر SettingsService.set()." + } + }, + _views: { + by_namespace: { + label: "حسب مساحة الاسم" + }, + tenant_only: { + label: "المستأجر" + }, + user_only: { + label: "المستخدم" + }, + all_settings: { + label: "الكل" + } + } + }, + sys_platform_setting: { + label: "إعداد المنصة", + pluralLabel: "إعدادات المنصة", + description: "قيم الإعدادات على مستوى النشر بأكمله: الدرجة العامة في تتالي الإعدادات.", + fields: { + id: { + label: "معرّف الإعداد" + }, + created_at: { + label: "تاريخ الإنشاء" + }, + updated_at: { + label: "تاريخ التحديث" + }, + namespace: { + label: "مساحة الاسم", + help: "مساحة اسم البيان (مثل mail، storage، ai)." + }, + key: { + label: "المفتاح", + help: "مفتاح المحدِّد داخل مساحة الاسم (snake_case)." + }, + value: { + label: "القيمة", + help: "قيمة مُرمَّزة بصيغة JSON. فارغة عندما يكون encrypted=true (انظر value_enc)." + }, + encrypted: { + label: "مُشفَّر", + help: "عندما تكون القيمة true، تُخزَّن القيمة مُشفَّرة أثناء التخزين في value_enc؛ ويكون عمود value فارغًا." + }, + locked: { + label: "مقفل", + help: "عندما تكون القيمة true، لا يمكن لصفوف المستأجر والمستخدم تجاوز هذه القيمة؛ وتُعيد عمليات الكتابة على تلك النطاقات 409." + }, + locked_reason: { + label: "سبب القفل", + help: "شرح مقروء يظهر في تلميح الواجهة عندما يكون locked=true." + }, + value_enc: { + label: "القيمة المُشفَّرة", + help: "مقبض صف sys_secret الذي يحمل النص المُشفَّر. يُعيَّن فقط عندما يكون encrypted=true." + }, + updated_by: { + label: "حدّثه", + help: "آخر جهة فاعلة كتبت هذا الصف عبر خدمة الإعدادات." + } + } + }, + sys_secret: { + label: "السر", + pluralLabel: "الأسرار", + description: "مخزن نصوص مُشفَّرة تكتبه ثلاث جهات منتجة مميَّزة (انظر managedBy)؛ تحتفظ كل منها بمقبضها في عمودها الخاص. لا يحتوي أبدًا على نص صريح.", + fields: { + id: { + label: "المعرّف", + help: "مقبض معتم. يقع المرجع في عمود الحامل الخاص بالجهة المنتجة الكاتبة — `sys_setting.value_enc`، أو مرجع `secret:` في صف أعمال، أو `credentialsRef` لمصدر بيانات — لذا فإن الصف غير المُشار إليه من `sys_setting` ليس بالضرورة غير مُشار إليه." + }, + created_at: { + label: "تاريخ الإنشاء", + help: "وقت كتابة النص المُشفَّر لأول مرة." + }, + rotated_at: { + label: "تاريخ التدوير", + help: "وقت آخر إعادة تغليف للنص المُشفَّر بمفتاح KMS جديد." + }, + namespace: { + label: "مساحة الاسم", + help: "تسمية مقيَّدة بنطاق الجهة المنتجة، وليست مساحة اسم إعدادات بالعموم: يكتب `SettingsService` مساحة اسم الإعدادات، ويكتب تشفير حقل `secret` في المحرك اسم الكائن، ويكتب رابط مصدر البيانات النطاق الذي يقدّمه المستدعي (الافتراضي `datasource`). انظر managedBy." + }, + key: { + label: "المفتاح", + help: "تسمية مقيَّدة بنطاق الجهة المنتجة تقترن بـ `namespace`: مفتاح محدِّد الإعدادات، أو اسم حقل `secret` المُشفَّر، أو اسم مصدر البيانات. يسجّل الزوج كيف خاطبت الجهة المنتجة القيمة؛ ولا يحدد أي جهة منتجة كتبت الصف." + }, + kms_key_id: { + label: "معرّف مفتاح KMS", + help: "مقبض KMS خارجي (ARN، أو معرّف مورد GCP، أو `local`)." + }, + alg: { + label: "الخوارزمية", + help: "وسم خوارزمية التشفير/AEAD." + }, + version: { + label: "الإصدار", + help: "يزداد في كل مرة تعيد فيها rotateKey() تغليف هذا الصف." + }, + ciphertext: { + label: "النص المُشفَّر", + help: "كتلة نص مُشفَّر بترميز المزوّد (base64 / JSON). تحددها التطبيقات؛ ولا يقرؤها إلا ICryptoProvider المطابق." + } + }, + _views: { + all: { + label: "كل الأسرار" + } + } + }, + sys_setting_audit: { + label: "إدخال تدقيق الإعداد", + pluralLabel: "تدقيق الإعدادات", + description: "سجل تدقيق إلحاقي فقط لتعديلات SettingsService.", + fields: { + id: { + label: "المعرّف" + }, + created_at: { + label: "تاريخ الإنشاء", + help: "وقت تسجيل التعديل." + }, + namespace: { + label: "مساحة الاسم" + }, + key: { + label: "المفتاح" + }, + scope: { + label: "النطاق", + help: "طبقة التتالي التي كُتب فيها الصف.", + options: { + tenant: "المستأجر", + user: "المستخدم" + } + }, + action: { + label: "الإجراء", + help: "نوع التعديل.", + options: { + set: "تعيين", + reset: "إعادة تعيين", + lock: "قفل", + unlock: "فتح القفل", + rotate: "تدوير" + } + }, + actor_id: { + label: "المنفِّذ", + help: "المستخدم الذي أجرى التعديل؛ فارغ لمهام النظام." + }, + source: { + label: "المصدر", + help: "نقطة دخول التعديل.", + options: { + ui: "واجهة المستخدم", + api: "API", + migration: "الترحيل", + import: "استيراد", + system: "النظام" + } + }, + reason: { + label: "السبب", + help: "مبرر نصي حر تقدّمه الجهة الفاعلة (اختياري)." + }, + old_hash: { + label: "التجزئة القديمة", + help: "ملخّص القيمة السابقة، وليس النص الصريح أبدًا: sha256: بترميز hex للقيمة العادية، والملخّص المفتاحي hmac-sha256: للسر. فارغ عند عدم تعيين قيمة سابقًا، أو للسر الذي ليس له ملخّص مفتاحي." + }, + new_hash: { + label: "التجزئة الجديدة", + help: "ملخّص القيمة الجديدة، وليس النص الصريح أبدًا: sha256: بترميز hex للقيمة العادية، والملخّص المفتاحي hmac-sha256: للسر. فارغ عند إعادة التعيين، أو للسر الذي ليس له ملخّص مفتاحي." + }, + encrypted: { + label: "مُشفَّر", + help: "true عندما يحمل الحقل مادة سرية (التدوير مهم)." + }, + request_id: { + label: "معرّف الطلب", + help: "يربط بـ sys_audit_log / التتبع." + } + }, + _views: { + recent: { + label: "الأحدث" + } + } + }, + sys_platform_setting_audit: { + label: "إدخال تدقيق إعداد المنصة", + pluralLabel: "تدقيق إعدادات المنصة", + description: "سجل تدقيق إلحاقي فقط لتعديلات SettingsService على المفاتيح ذات النطاق العام.", + fields: { + id: { + label: "المعرّف" + }, + created_at: { + label: "تاريخ الإنشاء", + help: "وقت تسجيل التعديل." + }, + namespace: { + label: "مساحة الاسم" + }, + key: { + label: "المفتاح" + }, + scope: { + label: "النطاق", + help: "طبقة التتالي التي كُتب فيها الصف — وهي هنا دائمًا الدرجة العامة.", + options: { + global: "عام" + } + }, + action: { + label: "الإجراء", + help: "نوع التعديل.", + options: { + set: "تعيين", + reset: "إعادة تعيين", + lock: "قفل", + unlock: "فتح القفل", + rotate: "تدوير" + } + }, + actor_id: { + label: "المنفِّذ", + help: "المستخدم الذي أجرى التعديل؛ فارغ لمهام النظام." + }, + source: { + label: "المصدر", + help: "نقطة دخول التعديل.", + options: { + ui: "واجهة المستخدم", + api: "API", + migration: "الترحيل", + import: "استيراد", + system: "النظام" + } + }, + reason: { + label: "السبب", + help: "مبرر نصي حر تقدّمه الجهة الفاعلة (اختياري)." + }, + old_hash: { + label: "التجزئة القديمة", + help: "ملخّص القيمة السابقة، وليس النص الصريح أبدًا: sha256: بترميز hex للقيمة العادية، والملخّص المفتاحي hmac-sha256: للسر. فارغ عند عدم تعيين قيمة سابقًا، أو للسر الذي ليس له ملخّص مفتاحي." + }, + new_hash: { + label: "التجزئة الجديدة", + help: "ملخّص القيمة الجديدة، وليس النص الصريح أبدًا: sha256: بترميز hex للقيمة العادية، والملخّص المفتاحي hmac-sha256: للسر. فارغ عند إعادة التعيين، أو للسر الذي ليس له ملخّص مفتاحي." + }, + encrypted: { + label: "مُشفَّر", + help: "true عندما يحمل الحقل مادة سرية (التدوير مهم)." + }, + request_id: { + label: "معرّف الطلب", + help: "يربط بـ sys_audit_log / التتبع." + } + }, + _views: { + recent: { + label: "الأحدث" + } + } + }, + sys_migration: { + label: "ترحيل البيانات", + pluralLabel: "عمليات ترحيل البيانات", + description: "علامات ترحيل البيانات على مستوى النشر: أي عمليات ترحيل بيانات مشروطة جرت هنا، وما إذا اجتاز فحصها الذاتي.", + fields: { + id: { + label: "معرّف الترحيل", + help: "معرّف ترحيل معروف (مثل adr-0104-file-references). صف واحد لكل ترحيل." + }, + last_run_at: { + label: "آخر تشغيل", + help: "وقت آخر إكمال لتشغيل مشروط (بوضع التطبيق) لهذا الترحيل على هذا النشر." + }, + verified_at: { + label: "تاريخ التحقق", + help: "وقت آخر نجاح للفحص الذاتي. فارغ إلى أن ينجح، ويُمسح مجددًا عند فشل تشغيل لاحق — فالتراجع يُغلق البوابة. يشترط المستهلكون أن يكون معيَّنًا وأن تكون blocking = 0." + }, + applied_at: { + label: "تاريخ التطبيق", + help: "وقت آخر تشغيل لملء البيانات بوضع التطبيق (مع تفعيل الكتابة)." + }, + blocking: { + label: "التناقضات الحاجبة", + help: "التناقضات الحاجبة التي أبلغ عنها آخر فحص ذاتي. تشترط البوابة 0." + }, + advisory: { + label: "النتائج الاستشارية", + help: "النتائج الاستشارية من آخر تشغيل (عناوين URL خارجية، مالكون قدامى، …) — تكلّف مساحة تخزين أو تحتاج إلى قرار نمذجة، لكنها لا تحجب البوابة أبدًا." + }, + details: { + label: "التفاصيل (JSON)", + help: "أعداد مُرمَّزة بصيغة JSON من آخر تشغيل، لأغراض التشخيص." + }, + deviation_observed_at: { + label: "تاريخ رصد الانحراف", + help: "آخر وقت قبِل فيه هذا النشر قيمة يرفضها عقده المُتحقَّق منه، عبر منفذ تجاوز OS_ALLOW_LAX_*. لا يمسح verified_at عمدًا: بل يسحب فقط النصف غير القابل للعكس مما تخوّله الشهادة — يتوقف حذف البايتات، ويستمر التحقق ووضع الشواهد. يُمسح عند التشغيل التالي بوضع التطبيق." + }, + deviation_detail: { + label: "تفاصيل الانحراف (JSON)", + help: "أول مثال مضاد مُرمَّز بصيغة JSON وراء deviation_observed_at (الكائن، الحقل، النوع، مشكلة التحليل)، ليتمكن المشغّل من إيجاد القيمة التي أغلقت البوابة غير القابلة للعكس." + }, + columns_moved_at: { + label: "تاريخ نقل الأعمدة", + help: "آخر وقت أكمل فيه هذا النشر نقل الأعمدة لهذا الترحيل — الخطوة التي تعيد تعيين أنواع الأعمدة المُرحَّلة وتعيد كتابة القيم التي تحملها بالترميز الجديد. دليل منفصل عن الحقلين applied_at، verified_at، اللذين يشهدان على ملء البيانات وفحصه الذاتي فقط: قد يحمل النشر كليهما ويظل يخزّن الترميز القديم. القيمة الفارغة تعني ذلك تمامًا، وهي حالة مستقرة متوقعة وليست خطأ — وهي ما يجب أن يفترضه المستهلك الذي لا يستطيع قراءة هذا الحقل." + }, + created_at: { + label: "تاريخ الإنشاء" + }, + updated_at: { + label: "تاريخ التحديث" + } + } + }, + sys_exchange_rate: { + label: "سعر الصرف", + pluralLabel: "أسعار الصرف", + description: "أسعار الصرف من العملة الأساسية للمؤسسة إلى عملة أجنبية، حسب تاريخ السريان — وهي الأسعار التي تُحوَّل بها المبالغ بالعملات الأجنبية إلى العملة الأساسية.", + fields: { + id: { + label: "المعرّف" + }, + currency: { + label: "العملة", + help: "رمز ISO 4217 للعملة الأجنبية، بأحرف كبيرة (EUR، JPY، CHF)." + }, + rate: { + label: "السعر", + help: "وحدة واحدة من العملة الأساسية للمؤسسة (الترجمة والإعدادات المحلية ← العملة الافتراضية) = عدد وحدات هذه العملة المساوي للسعر. مثال: العملة الأساسية USD، والعملة EUR، والسعر 0.92 يعني أن 1 USD = 0.92 EUR." + }, + effective_date: { + label: "تاريخ السريان", + help: "أول تاريخ يُطبَّق عليه هذا السعر. يبقى ساريًا إلى أن يحلّ محله صف لاحق للعملة نفسها." + }, + source: { + label: "المصدر", + help: "كيف وصل الصف: أُدخل يدويًا في الإعداد (manual)، أو حُمِّل من ملف (import)، أو كتبه تدفق تطبيق (app).", + options: { + manual: "يدوي", + import: "استيراد", + app: "التطبيق" + } + } + }, + _views: { + all_rates: { + label: "الكل", + description: "وحدة واحدة من العملة الأساسية للمؤسسة (الترجمة والإعدادات المحلية ← العملة الافتراضية) = عدد وحدات هذه العملة المساوي للسعر. مثال: العملة الأساسية USD، والعملة EUR، والسعر 0.92 يعني أن 1 USD = 0.92 EUR.", + emptyState: { + title: "لا توجد أسعار صرف بعد", + message: "أضف صفًا واحدًا لكل عملة عن كل تاريخ يتغيّر فيه سعرها، مثلًا صفًا لكل عملة في بداية كل شهر." + } + } + } + } +}; diff --git a/packages/platform-objects/src/apps/translations/ar.source-hashes.generated.ts b/packages/platform-objects/src/apps/translations/ar.source-hashes.generated.ts new file mode 100644 index 00000000000..3e3629b8d7e --- /dev/null +++ b/packages/platform-objects/src/apps/translations/ar.source-hashes.generated.ts @@ -0,0 +1,37 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +/** + * Auto-generated by 'os i18n extract' for locale 'ar'. Do not hand-edit. + * + * Each entry is the digest of the SOURCE REVISION that this locale's leaf at + * that path is still a byte copy of — provenance for the generated half of the + * bundles (commit 09b4f4e4e): a leaf whose digest no longer matches its source is stale and serves the source text instead. + * + * An entry exists only while the leaf IS such a copy. Re-translate the leaf in + * `.objects.generated.ts` and the next extract drops its entry by + * itself — the table makes no claim about text a translator wrote. A path with + * no entry is LEGACY-TRUSTED and never reported stale. + * + * ⚠️ Do not "fix" a staleness report by editing this file. Refreshing a digest + * records that the current text was copied from the current source, which is + * the false claim the mechanism exists to detect. Fix the TRANSLATION. + */ + +export const arGeneratedSourceHashes: Readonly> = { + "metadataForms.email_template.fields.fromOverride.helpText": "c5e29fa12ee1525b", + "metadataForms.email_template.fields.variables.helpText": "27197774c2cda633", + "metadataForms.permission.fields.fields.helpText": "887a0bae04a950e2", + "metadataForms.permission.fields.objects.helpText": "e2512cfb3b501aae", + "metadataForms.permission.fields.tabPermissions.helpText": "8942e2aebe59897c", + "metadataForms.position.fields.name.helpText": "3594bba7cc2a3c8a", + "objects.sys_email.fields.message_id.label": "14cd089a4062f4b1", + "objects.sys_job.fields.schedule_type.options.cron": "fd48d0e2cd59cab2", + "objects.sys_oauth_application.fields.jwks.label": "b2cf79f9d66c0dbc", + "objects.sys_oauth_application.fields.jwks_uri.label": "2a4339df734ab269", + "objects.sys_platform_setting_audit.fields.source.options.api": "4baeeff968e192d1", + "objects.sys_setting_audit.fields.source.options.api": "4baeeff968e192d1", + "objects.sys_sso_provider._actions.register_saml_provider.params.identifierFormat.placeholder": "5945ff9a6b644855", + "objects.sys_sso_provider._actions.register_sso_provider.params.mapEmail.placeholder": "66309c7adf5c9436", + "objects.sys_sso_provider._actions.register_sso_provider.params.mapName.placeholder": "a484c34aaf624bd6", + "objects.sys_sso_provider._actions.register_sso_provider.params.scopes.placeholder": "58ac90cde28c764d", +}; diff --git a/packages/platform-objects/src/apps/translations/ar.source-hashes.ts b/packages/platform-objects/src/apps/translations/ar.source-hashes.ts new file mode 100644 index 00000000000..feec662bc42 --- /dev/null +++ b/packages/platform-objects/src/apps/translations/ar.source-hashes.ts @@ -0,0 +1,196 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +/** + * Recorded source hashes for `ar` (#8765, Option B). + * + * Each entry is the digest of the `en.ts` SOURCE STRING that the `ar` + * translation at that path was made from. `setup.translation.ts` compares them + * against the current source when it assembles the served bundle: a mismatch + * marks that leaf stale and serves the source string in its place + * (`withSourceFallback`), which is the same degradation an untranslated key + * already produces. + * + * ## Maintaining this file + * + * **Re-translated a string?** Update the value in `ar.ts` AND the digest + * here — that pair is what "record the source hash at translation time" means, + * and it is what makes this locale recover on its own while the others keep + * falling back. The new digest is + * `collectSourceHashes(en)['']` (exported from `source-hash.ts`). + * + * **Added a brand-new translation?** No entry is needed. A path with no + * recorded hash is LEGACY-TRUSTED and served verbatim — the ruling's explicit + * rule, so that translations predating this mechanism did not all degrade the + * day it landed. An entry recorded now is strictly better (it will catch the + * NEXT source edit), but its absence never marks anything stale. + * + * ⚠️ Do not "fix" a mismatch by refreshing the digest alone. That records that + * the current translation was made from the current source, which is exactly + * the false claim this mechanism exists to detect — the served text would go + * back to being silently wrong. + * + * ## Which table wins for a path that appears in BOTH + * + * **This one.** A `--no-objects-only` extract records provenance for whatever + * sections it generated, so since #16872 a path under `apps` / `dashboards` / + * `pages` can carry an entry in `.source-hashes.generated.ts` as well + * as here. The two are read by two predicates over two tables and neither + * consults the other, so "wins" is a question about what a HUMAN maintains: + * maintain the digest HERE, exactly as this header already says, and ignore the + * generated one — it is regenerated from the tree on every extract and any + * edit to it is overwritten. + * + * The overlap cannot serve wrong text. The generated table only ever records a + * leaf whose bytes are still a copy of a source revision, so the only paths + * that can appear in both are the ones a translator deliberately left equal to + * the English source; for those, both predicates substitute the SAME source + * string and the served bytes are identical either way. Re-translate such a + * leaf and the next extract drops its generated entry by itself, leaving this + * table the only claim about it. + * + * Recorded when this locale was translated (#30512): every path the `ar` + * bundle authors carries the digest of the source string it was translated + * from, so the first source edit under any of them is caught here rather than + * served as a superseded translation. + */ +export const arSourceHashes: Readonly> = { + 'apps.account.label': 'ae88af048bfd5d64', + 'apps.account.description': 'd5fa9893662325fd', + 'apps.account.navigation.grp_account_inbox.label': '34dc7a0b58ada93b', + 'apps.account.navigation.grp_account_security.label': '0c463f40cccfdf86', + 'apps.account.navigation.grp_account_developer.label': '59668ec5af5afb55', + 'apps.account.navigation.grp_account_ai_assistant.label': 'cff764b13822a259', + 'apps.account.navigation.nav_account_profile.label': '2c92f019ece4d044', + 'apps.account.navigation.nav_account_notifications.label': 'e0fdb90b1d31078d', + 'apps.account.navigation.nav_account_approvals.label': '318a820df22a684b', + 'apps.account.navigation.nav_account_memberships.label': '86164fcd38e0378d', + 'apps.account.navigation.nav_account_linked.label': '58ca623b89e9ff5c', + 'apps.account.navigation.nav_account_sessions.label': '05a759e3a230615e', + 'apps.account.navigation.nav_account_api_keys.label': '8cda9851248b0490', + 'apps.account.navigation.nav_account_oauth_apps.label': '1b26cce61a4c10eb', + 'apps.account.navigation.nav_connect_agent.label': 'a4542422fb5e8a68', + 'apps.setup.label': 'e4bb0d8bcd47c273', + 'apps.setup.description': 'deacdfc679232a4a', + 'apps.setup.navigation.group_overview.label': 'eebdbe1cc7b59cdf', + 'apps.setup.navigation.group_apps.label': '699b367291abcbe6', + 'apps.setup.navigation.nav_packaged_actions.label': '0d86b11df972dff3', + 'apps.setup.navigation.nav_marketplace_browse.label': '2afbe021f5419fdd', + 'apps.setup.navigation.nav_marketplace_installed.label': '93bd5d458e05db60', + 'apps.setup.navigation.nav_cloud_connection.label': '3a7caad0cb98e71d', + 'apps.setup.navigation.group_people_org.label': 'd313f656b6f0ab65', + 'apps.setup.navigation.group_access_control.label': 'a1a4dd47bcbfaf67', + 'apps.setup.navigation.group_approvals.label': '318a820df22a684b', + 'apps.setup.navigation.group_configuration.label': '5e77c0af10f4252c', + 'apps.setup.navigation.group_integrations.label': '1c83392f57463173', + 'apps.setup.navigation.group_diagnostics.label': '62d648cd434b395c', + 'apps.setup.navigation.group_advanced.label': 'bc9e9a0da37ea229', + 'apps.setup.navigation.nav_system_overview.label': '574b2a4b7fb7f979', + 'apps.setup.navigation.nav_connect_agent.label': 'a4542422fb5e8a68', + 'apps.setup.navigation.nav_users.label': 'eee81f7307a66c40', + 'apps.setup.navigation.nav_organization.label': '3e55836156e1c1de', + 'apps.setup.navigation.nav_business_units.label': '46e0cd2344b40e77', + 'apps.setup.navigation.nav_teams.label': '3fe8519872f65108', + 'apps.setup.navigation.nav_organizations.label': '07488357631b973d', + 'apps.setup.navigation.nav_invitations.label': '4ebb9fd13de7a546', + 'apps.setup.navigation.nav_position_holders.label': '627cacb50fd1b0d8', + 'apps.setup.navigation.nav_permission_set_assignment.label': 'fcf7d1b69ee9c298', + 'apps.setup.navigation.nav_sharing_rules.label': 'ceaa66fa5f1df4ad', + 'apps.setup.navigation.nav_record_shares.label': 'e261d340ceb25b9a', + 'apps.setup.navigation.nav_queues.label': '226d23b8bd681e98', + 'apps.setup.navigation.nav_queue_members.label': '46715e86f5231603', + 'apps.setup.navigation.nav_assignment_rules.label': '1779fe384b23954f', + 'apps.setup.navigation.nav_sso_providers.label': '7b06b6a6abf53ba4', + 'apps.setup.navigation.nav_api_keys.label': '8cda9851248b0490', + 'apps.setup.navigation.nav_approvals_inbox.label': '0a9269b20d0266c9', + 'apps.setup.navigation.nav_approval_requests.label': 'aafd0f7c6a376f55', + 'apps.setup.navigation.nav_approval_actions.label': 'd2ebad7acfd50978', + 'apps.setup.navigation.nav_approval_delegations.label': 'fc0d41741659c462', + 'apps.setup.navigation.nav_settings_hub.label': 'a3ac65c7c7f461bf', + 'apps.setup.navigation.nav_settings_localization.label': 'beedfaf00452c829', + 'apps.setup.navigation.nav_exchange_rates.label': '6d043e1f08434720', + 'apps.setup.navigation.nav_settings_company.label': '9d7f685e11eaade9', + 'apps.setup.navigation.nav_settings_mail.label': '5d0599600587427d', + 'apps.setup.navigation.nav_settings_sms.label': '77a3056d33027ca0', + 'apps.setup.navigation.nav_settings_branding.label': 'ec6e391065514b64', + 'apps.setup.navigation.nav_settings_auth.label': '5c6d7dbd850ca37c', + 'apps.setup.navigation.nav_settings_storage.label': 'a2fb526b03c9a9e5', + 'apps.setup.navigation.nav_settings_ai.label': 'a6b41a554173f7ab', + 'apps.setup.navigation.nav_settings_knowledge.label': '140348539a50d9d5', + 'apps.setup.navigation.nav_settings_lifecycle.label': 'cd9fe9c930afed06', + 'apps.setup.navigation.nav_notification_preferences.label': '8869f324c1bd545a', + 'apps.setup.navigation.nav_notification_templates.label': '9e270c87ef2578df', + 'apps.setup.navigation.nav_sessions.label': '673bde6e0c8a3ef9', + 'apps.setup.navigation.nav_audit_logs.label': 'b2fad40733012b18', + 'apps.setup.navigation.nav_notifications.label': 'e0fdb90b1d31078d', + 'apps.setup.navigation.nav_webhooks.label': '8fdd81bc76459a6a', + 'apps.setup.navigation.nav_http_deliveries.label': '340f81dc6ea64987', + 'apps.setup.navigation.nav_datasources.label': '2f0648ce23158e6f', + 'apps.setup.navigation.nav_connector_credentials.label': '0504775224e05a81', + 'apps.setup.navigation.nav_connector_connections.label': '949e9a8fd25c3d57', + 'apps.setup.navigation.nav_oauth_apps.label': '1b26cce61a4c10eb', + 'apps.studio.label': '5aaaaea2294ee893', + 'apps.studio.description': 'c026a5795933995d', + 'apps.studio.navigation.group_overview.label': 'eebdbe1cc7b59cdf', + 'apps.studio.navigation.nav_app_builder.label': '128446e7650210e7', + 'apps.studio.navigation.nav_metadata_directory.label': '87f99e3de29ec25f', + 'apps.studio.navigation.nav_packages.label': '8cd8f29658025f73', + 'apps.studio.navigation.group_data_model.label': 'f7b1fa6f26b344e5', + 'apps.studio.navigation.nav_objects.label': '361008570f571b11', + 'apps.studio.navigation.group_ux.label': '32d21c81ff9312e2', + 'apps.studio.navigation.nav_apps.label': '699b367291abcbe6', + 'apps.studio.navigation.nav_views.label': 'e9c808e26b6a8d11', + 'apps.studio.navigation.nav_pages.label': 'cda91afda2acc4ab', + 'apps.studio.navigation.nav_dashboards.label': '6102d5e8e4bb4b7a', + 'apps.studio.navigation.nav_reports.label': 'f981f2f6d59fce84', + 'apps.studio.navigation.nav_datasets.label': '8491e1ea908b9b0d', + 'apps.studio.navigation.group_logic.label': '415c519f3ad330d7', + 'apps.studio.navigation.nav_actions.label': '7527654bedc9fdfc', + 'apps.studio.navigation.nav_hooks.label': '6fb7e3b7bc144f67', + 'apps.studio.navigation.group_automation.label': '1d5659130670f5f3', + 'apps.studio.navigation.nav_flows.label': '26f7b054faf636a2', + 'apps.studio.navigation.group_ai.label': '0227934c75e56686', + 'apps.studio.navigation.nav_agents.label': '5300d1df6736e399', + 'apps.studio.navigation.nav_tools.label': 'db86c97f27cd31ce', + 'apps.studio.navigation.nav_skills.label': 'eb2af90cd11bbd34', + 'apps.studio.navigation.group_developer.label': '59668ec5af5afb55', + 'apps.studio.navigation.nav_api_console.label': 'd90e508f1921e8d3', + 'apps.studio.navigation.nav_flow_runs.label': '0bdee1504c505862', + 'apps.studio.navigation.nav_public_forms.label': '4592e97959994e86', + 'apps.studio.navigation.nav_integrations.label': 'edbd97d7f401a5b8', + 'apps.studio.navigation.group_integration.label': 'a1f2ddfb5c00c83a', + 'apps.studio.navigation.nav_email_templates.label': '3463bce831c833d5', + 'dashboards.system_overview.label': '574b2a4b7fb7f979', + 'dashboards.system_overview.description': '56dbd944c4b834da', + 'dashboards.system_overview.widgets.widget_total_users.title': 'cee2b388d39f4b4d', + 'dashboards.system_overview.widgets.widget_total_users.description': '54777631b0239fa1', + 'dashboards.system_overview.widgets.widget_organizations.title': '07488357631b973d', + 'dashboards.system_overview.widgets.widget_organizations.description': '4c8ffa2d407f4410', + 'dashboards.system_overview.widgets.widget_active_sessions.title': '05a759e3a230615e', + 'dashboards.system_overview.widgets.widget_active_sessions.description': '72335b16a1f8624e', + 'dashboards.system_overview.widgets.widget_packages_installed.title': 'ca0ce2bb712d83b3', + 'dashboards.system_overview.widgets.widget_packages_installed.description': '67c5558bf10fcb4d', + 'dashboards.system_overview.widgets.widget_login_events.title': 'c6475b3ee5d1efc1', + 'dashboards.system_overview.widgets.widget_login_events.description': '3c41545dbbd34f3c', + 'dashboards.system_overview.widgets.widget_config_changes.title': '4ca2f591d9440211', + 'dashboards.system_overview.widgets.widget_config_changes.description': '6be1d6643282e176', + 'dashboards.system_overview.widgets.widget_events_by_type.title': 'accefd73f7c4187d', + 'dashboards.system_overview.widgets.widget_events_by_type.description': '013308726e1bd2d3', + 'dashboards.system_overview.widgets.widget_events_by_user.title': '04d5cab03abee166', + 'dashboards.system_overview.widgets.widget_events_by_user.description': 'ee9e0d412f66fb4d', + 'dashboards.system_overview.widgets.widget_recent_events.title': '5fb9db734193fe37', + 'dashboards.system_overview.widgets.widget_recent_events.description': '5b9ce7479f6c178c', + 'dashboards.system_overview.globalFilters.created_at.label': 'dcba3619736cb533', + 'datasets.sys_user_metrics.measures.user_count.label': 'eee81f7307a66c40', + 'datasets.sys_organization_metrics.measures.org_count.label': '07488357631b973d', + 'datasets.sys_session_metrics.measures.session_count.label': '673bde6e0c8a3ef9', + 'datasets.sys_package_installation_metrics.measures.package_count.label': 'bd7a67c9d5b3ac44', + 'datasets.sys_audit_log_metrics.dimensions.action.label': 'a36bba313a7aa9ab', + 'datasets.sys_audit_log_metrics.dimensions.user_id.label': '6c0d22a931e235bd', + 'datasets.sys_audit_log_metrics.measures.event_count.label': '41e7c95aaa871569', + 'pages.cloud_connection_settings.label': '3a7caad0cb98e71d', + 'pages.cloud_connection_settings.subtitle': 'f5cdd253e8ffef9f', + 'pages.connect_agent.label': 'a4542422fb5e8a68', + 'pages.connect_agent.subtitle': 'b0d7c6d174511bcb', + 'pages.sys_user_detail.label': '6c0d22a931e235bd', + 'pages.sys_organization_detail.label': '3e55836156e1c1de', +}; diff --git a/packages/platform-objects/src/apps/translations/ar.ts b/packages/platform-objects/src/apps/translations/ar.ts new file mode 100644 index 00000000000..8e3accce707 --- /dev/null +++ b/packages/platform-objects/src/apps/translations/ar.ts @@ -0,0 +1,210 @@ +// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. + +import type { TranslationData } from '@objectstack/spec/system'; +import { arObjects } from './ar.objects.generated.js'; + +/** + * العربية (ar) — Setup App Translations + */ +export const ar: TranslationData = { + objects: arObjects, + apps: { + account: { + label: 'الحساب', + description: 'إعدادات الأمان والهوية الشخصية', + navigation: { + grp_account_inbox: { label: 'صندوق الوارد' }, + grp_account_security: { label: 'الأمان' }, + grp_account_developer: { label: 'المطوّر' }, + grp_account_ai_assistant: { label: 'مساعد الذكاء الاصطناعي' }, + nav_account_profile: { label: 'الملف الشخصي' }, + nav_account_notifications: { label: 'الإشعارات' }, + nav_account_approvals: { label: 'بانتظار موافقتي' }, + nav_account_memberships: { label: 'مؤسساتي' }, + nav_account_linked: { label: 'الحسابات المرتبطة' }, + nav_account_sessions: { label: 'الجلسات النشطة' }, + nav_account_api_keys: { label: 'مفاتيح API' }, + nav_account_oauth_apps: { label: 'تطبيقات OAuth' }, + // Contributed at runtime by `@objectstack/mcp` into + // `grp_account_ai_assistant`; the Setup twin's key does not answer for the + // Account app (one `apps..navigation` namespace per app). + nav_connect_agent: { label: 'ربط مساعد ذكاء اصطناعي' }, + }, + }, + setup: { + label: 'الإعداد', + description: 'إعدادات المنصة وإدارتها', + navigation: { + group_overview: { label: 'نظرة عامة' }, + group_apps: { label: 'التطبيقات' }, + nav_packaged_actions: { label: 'الإجراءات المضمَّنة في الحزم' }, + nav_marketplace_browse: { label: 'تصفح السوق' }, + nav_marketplace_installed: { label: 'التطبيقات المثبّتة' }, + nav_cloud_connection: { label: 'الاتصال السحابي' }, + group_people_org: { label: 'الأشخاص والمؤسسة' }, + group_access_control: { label: 'التحكم في الوصول' }, + group_approvals: { label: 'الموافقات' }, + group_configuration: { label: 'التهيئة' }, + group_integrations: { label: 'عمليات التكامل' }, + group_diagnostics: { label: 'التشخيص' }, + group_advanced: { label: 'متقدم' }, + + nav_system_overview: { label: 'نظرة عامة على النظام' }, + nav_connect_agent: { label: 'ربط مساعد ذكاء اصطناعي' }, + + nav_users: { label: 'المستخدمون' }, + nav_organization: { label: 'المؤسسة' }, + nav_business_units: { label: 'وحدات الأعمال' }, + nav_teams: { label: 'الفرق' }, + nav_organizations: { label: 'المؤسسات' }, + nav_invitations: { label: 'الدعوات' }, + nav_position_holders: { label: 'شاغلو المناصب' }, + nav_permission_set_assignment: { label: 'إسناد مجموعات الأذونات' }, + + nav_sharing_rules: { label: 'قواعد المشاركة' }, + nav_record_shares: { label: 'مشاركات السجلات' }, + nav_queues: { label: 'قوائم الانتظار' }, + nav_queue_members: { label: 'أعضاء قائمة الانتظار' }, + nav_assignment_rules: { label: 'قواعد الإسناد' }, + // Conditionally contributed by `@objectstack/plugin-auth` (only when an + // external IdP is wired), so `check:app-nav-i18n` cannot see it — see + // the rationale in `en.ts` (#6659). Wording matches this locale's + // `sys_sso_provider.pluralLabel`. + nav_sso_providers: { label: 'مزوّدو SSO' }, + nav_api_keys: { label: 'مفاتيح API' }, + + // صندوق الموافقات = the Approvals Inbox component (#7234) — the surface + // with decision actions. The three below are the engine's raw tables; the + // first and last read as plugin-approvals' ar object plural labels. + nav_approvals_inbox: { label: 'صندوق الموافقات' }, + nav_approval_requests: { label: 'طلبات الموافقة' }, + nav_approval_actions: { label: 'سجل الموافقات' }, + // `تفويضات الموافقة` matches sys_approval_delegation's object plural + // label; `(خارج المكتب)` renders the entry's own "(OOO)" qualifier. + nav_approval_delegations: { label: 'تفويضات الموافقة (خارج المكتب)' }, + + // Each nav_settings_* entry reads as its hub card's title in + // service-settings' ar bundle (`setup-nav-hub-parity.test.ts`), so the + // two delivery pages say إرسال here while the HTTP log below says تسليم, + // as sys_http_delivery's ar object label does. + nav_settings_hub: { label: 'جميع الإعدادات' }, + nav_settings_localization: { label: 'الترجمة والإعدادات المحلية' }, + nav_exchange_rates: { label: 'أسعار الصرف' }, + nav_settings_company: { label: 'الشركة' }, + nav_settings_mail: { label: 'إرسال البريد' }, + nav_settings_sms: { label: 'إرسال الرسائل النصية' }, + nav_settings_branding: { label: 'العلامة التجارية' }, + nav_settings_auth: { label: 'المصادقة' }, + nav_settings_access_policy: { label: 'سياسة الوصول' }, + nav_settings_storage: { label: 'تخزين الملفات' }, + nav_settings_ai: { label: 'الذكاء الاصطناعي والتضمين (Embedder)' }, + nav_settings_knowledge: { label: 'المعرفة' }, + nav_settings_lifecycle: { label: 'دورة حياة البيانات' }, + nav_notification_preferences: { label: 'تفضيلات الإشعارات' }, + nav_notification_templates: { label: 'قوالب الإشعارات' }, + + nav_sessions: { label: 'الجلسات' }, + nav_audit_logs: { label: 'سجل التدقيق' }, + nav_notifications: { label: 'الإشعارات' }, + + // Integrations — contributed at RUNTIME by the owning capability + // plugins (ADR-0029 K2), so no static walk of SETUP_APP can see them. + // `خطافات الويب` is sys_webhook's own ar object plural label, and this + // bundle is where that choice is RECORDED rather than reached by falling + // back (#5750). + nav_webhooks: { label: 'خطافات الويب' }, + nav_http_deliveries: { label: 'عمليات تسليم HTTP' }, + nav_datasources: { label: 'مصادر البيانات' }, + nav_connector_credentials: { label: 'بيانات اعتماد الموصلات' }, + nav_connector_connections: { label: 'اتصالات الموصلات' }, + + nav_oauth_apps: { label: 'تطبيقات OAuth' }, + }, + }, + studio: { + label: 'Studio', + description: 'منضدة عمل البيانات الوصفية للمطوّرين والمحللين والمنفّذين', + navigation: { + group_overview: { label: 'نظرة عامة' }, + nav_app_builder: { label: 'منشئ التطبيقات' }, + nav_metadata_directory: { label: 'كل أنواع البيانات الوصفية' }, + nav_packages: { label: 'الحزم' }, + group_data_model: { label: 'نموذج البيانات' }, + nav_objects: { label: 'الكائنات' }, + group_ux: { label: 'تجربة المستخدم' }, + nav_apps: { label: 'التطبيقات' }, + nav_views: { label: 'طرق العرض' }, + nav_pages: { label: 'الصفحات' }, + nav_dashboards: { label: 'لوحات المعلومات' }, + nav_reports: { label: 'التقارير' }, + nav_datasets: { label: 'مجموعات البيانات' }, + group_logic: { label: 'المنطق' }, + nav_actions: { label: 'الإجراءات' }, + nav_hooks: { label: 'الخطافات' }, + group_automation: { label: 'الأتمتة' }, + nav_flows: { label: 'التدفقات' }, + group_ai: { label: 'الذكاء الاصطناعي' }, + nav_agents: { label: 'الوكلاء' }, + nav_tools: { label: 'الأدوات' }, + nav_skills: { label: 'المهارات' }, + group_developer: { label: 'المطوّر' }, + nav_api_console: { label: 'وحدة تحكم API' }, + nav_flow_runs: { label: 'عمليات تشغيل التدفقات' }, + nav_public_forms: { label: 'النماذج العامة' }, + nav_integrations: { label: 'عمليات التكامل وواجهات API' }, + group_integration: { label: 'التكامل' }, + nav_email_templates: { label: 'قوالب البريد الإلكتروني' }, + }, + }, + }, + + dashboards: { + system_overview: { + label: 'نظرة عامة على النظام', + description: 'صحة المنصة، ونشاط الأمان، وأحداث التدقيق الأخيرة', + widgets: { + widget_total_users: { title: 'إجمالي المستخدمين', description: 'إجمالي المستخدمين المسجّلين في النظام' }, + widget_organizations: { title: 'المؤسسات', description: 'إجمالي المؤسسات على المنصة' }, + widget_active_sessions: { title: 'الجلسات النشطة', description: 'عدد جلسات المستخدمين النشطة حاليًا' }, + widget_packages_installed: { title: 'الحزم المثبّتة', description: 'عمليات تثبيت الحزم النشطة عبر المشاريع' }, + widget_login_events: { title: 'أحداث تسجيل الدخول', description: 'أحداث المصادقة التي يسجّلها سجل التدقيق' }, + widget_config_changes: { title: 'تغييرات التهيئة', description: 'تعديلات تهيئة النظام' }, + widget_events_by_type: { title: 'أحداث التدقيق حسب الإجراء', description: 'توزيع أحداث التدقيق حسب نوع الإجراء' }, + widget_events_by_user: { title: 'الأحداث حسب المستخدم', description: 'توزيع النشاط بين المستخدمين' }, + widget_recent_events: { title: 'حجم الأحداث حسب الإجراء', description: 'حجم الأحداث مجمَّعًا حسب الإجراء (login، logout، config، …)' }, + }, + // The date-range bar above the widgets — addressable since + // `dashboards..globalFilters` (#16772), keyed by the filter's + // `field` because it authors no `name`. + globalFilters: { + created_at: { label: 'النطاق الزمني' }, + }, + }, + }, + + datasets: { + sys_user_metrics: { measures: { user_count: { label: 'المستخدمون' } } }, + sys_organization_metrics: { measures: { org_count: { label: 'المؤسسات' } } }, + sys_session_metrics: { measures: { session_count: { label: 'الجلسات' } } }, + sys_package_installation_metrics: { measures: { package_count: { label: 'عمليات التثبيت' } } }, + sys_audit_log_metrics: { + dimensions: { action: { label: 'الإجراء' }, user_id: { label: 'المستخدم' } }, + measures: { event_count: { label: 'الأحداث' } }, + }, + }, + + pages: { + cloud_connection_settings: { + label: 'الاتصال السحابي', + subtitle: 'اربط وقت التشغيل هذا بمستوى تحكم ObjectStack لتصفح الحزم الخاصة بمؤسستك وتثبيتها هنا.', + }, + connect_agent: { + label: 'ربط مساعد ذكاء اصطناعي', + subtitle: 'امنح أي عميل ذكاء اصطناعي يدعم MCP وصولًا محكومًا إلى هذا النظام — كل استدعاء يُنفَّذ بأذونات المستدعي نفسه وأمانه على مستوى الصف.', + }, + + // Platform record pages (#15743) -- see `en.ts` for the key set. + sys_user_detail: { label: 'المستخدم' }, + sys_organization_detail: { label: 'المؤسسة' }, + }, +}; diff --git a/packages/platform-objects/src/apps/translations/bu-rename-consistency.test.ts b/packages/platform-objects/src/apps/translations/bu-rename-consistency.test.ts index 639c1d32238..962c7893df0 100644 --- a/packages/platform-objects/src/apps/translations/bu-rename-consistency.test.ts +++ b/packages/platform-objects/src/apps/translations/bu-rename-consistency.test.ts @@ -15,6 +15,7 @@ import { zhCNObjects } from './zh-CN.objects.generated.js'; import { jaJPObjects } from './ja-JP.objects.generated.js'; import { esESObjects } from './es-ES.objects.generated.js'; import { zhHantObjects } from './zh-Hant.objects.generated.js'; +import { arObjects } from './ar.objects.generated.js'; const LOCALES = [ { name: 'en', objs: enObjects as Record, old: /\bDepartment\b/ }, @@ -22,6 +23,7 @@ const LOCALES = [ { name: 'ja-JP', objs: jaJPObjects as Record, old: /部門/ }, { name: 'es-ES', objs: esESObjects as Record, old: /Departamento/ }, { name: 'zh-Hant', objs: zhHantObjects as Record, old: /部門/ }, + { name: 'ar', objs: arObjects as Record, old: /قسم/ }, ]; const OBJECTS = ['sys_business_unit', 'sys_business_unit_member']; diff --git a/packages/platform-objects/src/apps/translations/confirm-question-carryover.test.ts b/packages/platform-objects/src/apps/translations/confirm-question-carryover.test.ts index cedad533e4d..893f16b6a97 100644 --- a/packages/platform-objects/src/apps/translations/confirm-question-carryover.test.ts +++ b/packages/platform-objects/src/apps/translations/confirm-question-carryover.test.ts @@ -26,12 +26,14 @@ import { zhCNObjects } from './zh-CN.objects.generated.js'; import { jaJPObjects } from './ja-JP.objects.generated.js'; import { esESObjects } from './es-ES.objects.generated.js'; import { zhHantObjects } from './zh-Hant.objects.generated.js'; +import { arObjects } from './ar.objects.generated.js'; const LOCALES = [ ['zh-CN', zhCNObjects], ['ja-JP', jaJPObjects], ['es-ES', esESObjects], ['zh-Hant', zhHantObjects], + ['ar', arObjects], ] as const; /** diff --git a/packages/platform-objects/src/apps/translations/dashboard-header-children.test.ts b/packages/platform-objects/src/apps/translations/dashboard-header-children.test.ts index 8726e34b853..ecd9a1f2525 100644 --- a/packages/platform-objects/src/apps/translations/dashboard-header-children.test.ts +++ b/packages/platform-objects/src/apps/translations/dashboard-header-children.test.ts @@ -25,6 +25,7 @@ import { zhCNMetadataForms } from './zh-CN.metadata-forms.generated.js'; import { jaJPMetadataForms } from './ja-JP.metadata-forms.generated.js'; import { esESMetadataForms } from './es-ES.metadata-forms.generated.js'; import { zhHantMetadataForms } from './zh-Hant.metadata-forms.generated.js'; +import { arMetadataForms } from './ar.metadata-forms.generated.js'; const LOCALES = [ { name: 'en', forms: enMetadataForms as Record }, @@ -32,6 +33,7 @@ const LOCALES = [ { name: 'ja-JP', forms: jaJPMetadataForms as Record }, { name: 'es-ES', forms: esESMetadataForms as Record }, { name: 'zh-Hant', forms: zhHantMetadataForms as Record }, + { name: 'ar', forms: arMetadataForms as Record }, ]; const HEADER_CHILDREN = ['header.showTitle', 'header.showDescription', 'header.actions']; diff --git a/packages/platform-objects/src/apps/translations/index.ts b/packages/platform-objects/src/apps/translations/index.ts index b3978113156..38ad65732ea 100644 --- a/packages/platform-objects/src/apps/translations/index.ts +++ b/packages/platform-objects/src/apps/translations/index.ts @@ -10,6 +10,7 @@ export { zhCN } from './zh-CN.js'; export { jaJP } from './ja-JP.js'; export { esES } from './es-ES.js'; export { zhHant } from './zh-Hant.js'; +export { ar } from './ar.js'; /** * The staleness mechanism (#8765 Option B, extended to the generated bundles by diff --git a/packages/platform-objects/src/apps/translations/metadata-forms-vocabulary.test.ts b/packages/platform-objects/src/apps/translations/metadata-forms-vocabulary.test.ts index b220a54f1e9..f01ff83ea3b 100644 --- a/packages/platform-objects/src/apps/translations/metadata-forms-vocabulary.test.ts +++ b/packages/platform-objects/src/apps/translations/metadata-forms-vocabulary.test.ts @@ -15,6 +15,7 @@ import { zhCNMetadataForms } from './zh-CN.metadata-forms.generated.js'; import { jaJPMetadataForms } from './ja-JP.metadata-forms.generated.js'; import { esESMetadataForms } from './es-ES.metadata-forms.generated.js'; import { zhHantMetadataForms } from './zh-Hant.metadata-forms.generated.js'; +import { arMetadataForms } from './ar.metadata-forms.generated.js'; const LOCALES = [ { name: 'en', forms: enMetadataForms as Record }, @@ -22,6 +23,7 @@ const LOCALES = [ { name: 'ja-JP', forms: jaJPMetadataForms as Record }, { name: 'es-ES', forms: esESMetadataForms as Record }, { name: 'zh-Hant', forms: zhHantMetadataForms as Record }, + { name: 'ar', forms: arMetadataForms as Record }, ]; describe('ADR-0090 D2/D3 — metadata-form translations use the v2 vocabulary', () => { diff --git a/packages/platform-objects/src/apps/translations/repeater-row-properties.test.ts b/packages/platform-objects/src/apps/translations/repeater-row-properties.test.ts index 1870948b809..d95ca849435 100644 --- a/packages/platform-objects/src/apps/translations/repeater-row-properties.test.ts +++ b/packages/platform-objects/src/apps/translations/repeater-row-properties.test.ts @@ -54,6 +54,7 @@ import { zhCNMetadataForms } from './zh-CN.metadata-forms.generated.js'; import { jaJPMetadataForms } from './ja-JP.metadata-forms.generated.js'; import { esESMetadataForms } from './es-ES.metadata-forms.generated.js'; import { zhHantMetadataForms } from './zh-Hant.metadata-forms.generated.js'; +import { arMetadataForms } from './ar.metadata-forms.generated.js'; const LOCALES = [ { name: 'en', forms: enMetadataForms as Record }, @@ -61,6 +62,7 @@ const LOCALES = [ { name: 'ja-JP', forms: jaJPMetadataForms as Record }, { name: 'es-ES', forms: esESMetadataForms as Record }, { name: 'zh-Hant', forms: zhHantMetadataForms as Record }, + { name: 'ar', forms: arMetadataForms as Record }, ]; /** Every `*.form.ts` the spec package exports, by its export name. */ diff --git a/packages/platform-objects/src/apps/translations/setup-nav-dead-key-tombstone.test.ts b/packages/platform-objects/src/apps/translations/setup-nav-dead-key-tombstone.test.ts index 0cd1f9e15ba..c7d623ac7fc 100644 --- a/packages/platform-objects/src/apps/translations/setup-nav-dead-key-tombstone.test.ts +++ b/packages/platform-objects/src/apps/translations/setup-nav-dead-key-tombstone.test.ts @@ -107,8 +107,9 @@ import { zhCN } from './zh-CN.js'; import { jaJP } from './ja-JP.js'; import { esES } from './es-ES.js'; import { zhHant } from './zh-Hant.js'; +import { ar } from './ar.js'; -const LOCALES = { en, 'zh-CN': zhCN, 'ja-JP': jaJP, 'es-ES': esES, 'zh-Hant': zhHant } as const; +const LOCALES = { en, 'zh-CN': zhCN, 'ja-JP': jaJP, 'es-ES': esES, 'zh-Hant': zhHant, ar } as const; /** Removed Setup nav ids. Delete a line here only together with its nav item. */ const DEAD_SETUP_NAV_IDS = [ diff --git a/packages/platform-objects/src/apps/translations/setup.translation.ts b/packages/platform-objects/src/apps/translations/setup.translation.ts index 974357305b9..c346aac1724 100644 --- a/packages/platform-objects/src/apps/translations/setup.translation.ts +++ b/packages/platform-objects/src/apps/translations/setup.translation.ts @@ -6,28 +6,31 @@ import { zhCN } from './zh-CN.js'; import { jaJP } from './ja-JP.js'; import { esES } from './es-ES.js'; import { zhHant } from './zh-Hant.js'; +import { ar } from './ar.js'; import { zhCNSourceHashes } from './zh-CN.source-hashes.js'; import { jaJPSourceHashes } from './ja-JP.source-hashes.js'; import { esESSourceHashes } from './es-ES.source-hashes.js'; import { zhHantSourceHashes } from './zh-Hant.source-hashes.js'; +import { arSourceHashes } from './ar.source-hashes.js'; import { zhCNGeneratedSourceHashes } from './zh-CN.source-hashes.generated.js'; import { jaJPGeneratedSourceHashes } from './ja-JP.source-hashes.generated.js'; import { esESGeneratedSourceHashes } from './es-ES.source-hashes.generated.js'; import { zhHantGeneratedSourceHashes } from './zh-Hant.source-hashes.generated.js'; +import { arGeneratedSourceHashes } from './ar.source-hashes.generated.js'; import { withSourceFallback } from './source-hash.js'; /** * Setup App — Internationalization (i18n) * * Mirrors the CRM example's `per_locale` convention: each language lives - * in its own file (`en.ts`, `zh-CN.ts`, `ja-JP.ts`, `es-ES.ts`, `zh-Hant.ts`) and is + * in its own file (`en.ts`, `zh-CN.ts`, `ja-JP.ts`, `es-ES.ts`, `zh-Hant.ts`, `ar.ts`) and is * assembled into a single `TranslationBundle` here. * * Loaded into the kernel's i18n service by `plugin-auth` during * `kernel:ready` (auth is the natural registration point for the Setup * App — see `auth-plugin.ts`). * - * Supported locales: en, zh-CN, ja-JP, es-ES, zh-Hant. + * Supported locales: en, zh-CN, ja-JP, es-ES, zh-Hant, ar. * * ## This assembly is where staleness is resolved (#8765) * @@ -73,4 +76,5 @@ export const SetupAppTranslations: TranslationBundle = { 'ja-JP': withSourceFallback(jaJP, en, jaJPSourceHashes, jaJPGeneratedSourceHashes), 'es-ES': withSourceFallback(esES, en, esESSourceHashes, esESGeneratedSourceHashes), 'zh-Hant': withSourceFallback(zhHant, en, zhHantSourceHashes, zhHantGeneratedSourceHashes), + ar: withSourceFallback(ar, en, arSourceHashes, arGeneratedSourceHashes), }; diff --git a/packages/platform-objects/src/apps/translations/source-hash.test.ts b/packages/platform-objects/src/apps/translations/source-hash.test.ts index d34bb1a081d..04a29568afc 100644 --- a/packages/platform-objects/src/apps/translations/source-hash.test.ts +++ b/packages/platform-objects/src/apps/translations/source-hash.test.ts @@ -24,10 +24,12 @@ import { zhCN } from './zh-CN.js'; import { jaJP } from './ja-JP.js'; import { esES } from './es-ES.js'; import { zhHant } from './zh-Hant.js'; +import { ar } from './ar.js'; import { zhCNSourceHashes } from './zh-CN.source-hashes.js'; import { jaJPSourceHashes } from './ja-JP.source-hashes.js'; import { esESSourceHashes } from './es-ES.source-hashes.js'; import { zhHantSourceHashes } from './zh-Hant.source-hashes.js'; +import { arSourceHashes } from './ar.source-hashes.js'; import { SetupAppTranslations } from './setup.translation.js'; import { collectFilledFromHashes, @@ -42,6 +44,7 @@ import { zhCNGeneratedSourceHashes } from './zh-CN.source-hashes.generated.js'; import { jaJPGeneratedSourceHashes } from './ja-JP.source-hashes.generated.js'; import { esESGeneratedSourceHashes } from './es-ES.source-hashes.generated.js'; import { zhHantGeneratedSourceHashes } from './zh-Hant.source-hashes.generated.js'; +import { arGeneratedSourceHashes } from './ar.source-hashes.generated.js'; // A source bundle and a translation of it, at one nav leaf per app so the // per-locale independence claim has something to be independent about. @@ -191,6 +194,7 @@ describe('the shipped bundles', () => { ['ja-JP', jaJP, jaJPSourceHashes], ['es-ES', esES, esESSourceHashes], ['zh-Hant', zhHant, zhHantSourceHashes], + ['ar', ar, arSourceHashes], ] as const; // ⭐ The acceptance criterion, applied to the real data: B introduces NO new @@ -389,6 +393,7 @@ describe('the committed provenance companions', () => { 'ja-JP': jaJPGeneratedSourceHashes, 'es-ES': esESGeneratedSourceHashes, 'zh-Hant': zhHantGeneratedSourceHashes, + ar: arGeneratedSourceHashes, } as const; // ⚠️ Deliberately NOT asserted here: "no shipped leaf is stale". That claim diff --git a/packages/platform-objects/src/identity/invitation-delivery-copy.test.ts b/packages/platform-objects/src/identity/invitation-delivery-copy.test.ts index 6939fb02bc7..9391cf9fd71 100644 --- a/packages/platform-objects/src/identity/invitation-delivery-copy.test.ts +++ b/packages/platform-objects/src/identity/invitation-delivery-copy.test.ts @@ -30,6 +30,7 @@ import { zhCNObjects } from '../apps/translations/zh-CN.objects.generated.js'; import { jaJPObjects } from '../apps/translations/ja-JP.objects.generated.js'; import { esESObjects } from '../apps/translations/es-ES.objects.generated.js'; import { zhHantObjects } from '../apps/translations/zh-Hant.objects.generated.js'; +import { arObjects } from '../apps/translations/ar.objects.generated.js'; import { INVITE_USER_OUTCOME_MESSAGES, RESEND_INVITATION_OUTCOME_MESSAGES } from './invite-outcome-messages.js'; import { SysInvitation } from './sys-invitation.object.js'; import { SysMember } from './sys-member.object.js'; @@ -79,6 +80,7 @@ const TRANSLATED = [ ['ja-JP', jaJPObjects], ['es-ES', esESObjects], ['zh-Hant', zhHantObjects], + ['ar', arObjects], ] as const; /** Any spelling of "sent" a `link_only` copy must not carry. */ diff --git a/packages/platform-objects/src/identity/invitation-screens-copy.test.ts b/packages/platform-objects/src/identity/invitation-screens-copy.test.ts index b593ab3c68e..a62a3b08f5b 100644 --- a/packages/platform-objects/src/identity/invitation-screens-copy.test.ts +++ b/packages/platform-objects/src/identity/invitation-screens-copy.test.ts @@ -20,6 +20,7 @@ import { zhCNObjects } from '../apps/translations/zh-CN.objects.generated.js'; import { jaJPObjects } from '../apps/translations/ja-JP.objects.generated.js'; import { esESObjects } from '../apps/translations/es-ES.objects.generated.js'; import { zhHantObjects } from '../apps/translations/zh-Hant.objects.generated.js'; +import { arObjects } from '../apps/translations/ar.objects.generated.js'; import { MEMBERSHIP_ROLE_PARAM_HELP } from './membership-role-help.js'; import { SysInvitation } from './sys-invitation.object.js'; import { SysMember } from './sys-member.object.js'; @@ -79,6 +80,7 @@ const TRANSLATED = [ ['ja-JP', jaJPObjects], ['es-ES', esESObjects], ['zh-Hant', zhHantObjects], + ['ar', arObjects], ] as const; const bundleObject = (bundle: unknown, object: string): Record | undefined => diff --git a/packages/platform-objects/src/identity/invite-user-action-copy.test.ts b/packages/platform-objects/src/identity/invite-user-action-copy.test.ts index e4da48d0583..30db7026bd6 100644 --- a/packages/platform-objects/src/identity/invite-user-action-copy.test.ts +++ b/packages/platform-objects/src/identity/invite-user-action-copy.test.ts @@ -30,6 +30,7 @@ import { zhCNObjects } from '../apps/translations/zh-CN.objects.generated.js'; import { jaJPObjects } from '../apps/translations/ja-JP.objects.generated.js'; import { esESObjects } from '../apps/translations/es-ES.objects.generated.js'; import { zhHantObjects } from '../apps/translations/zh-Hant.objects.generated.js'; +import { arObjects } from '../apps/translations/ar.objects.generated.js'; import { SysInvitation } from './sys-invitation.object.js'; import { SysMember } from './sys-member.object.js'; import { SysOrganization } from './sys-organization.object.js'; @@ -78,6 +79,7 @@ const TRANSLATED = [ ['ja-JP', jaJPObjects], ['es-ES', esESObjects], ['zh-Hant', zhHantObjects], + ['ar', arObjects], ] as const; // [#24814] `successMessage` is a `template` slot: the parsed diff --git a/packages/platform-objects/src/identity/sys-api-key-action-copy.test.ts b/packages/platform-objects/src/identity/sys-api-key-action-copy.test.ts index f2340874470..0e7dff3686d 100644 --- a/packages/platform-objects/src/identity/sys-api-key-action-copy.test.ts +++ b/packages/platform-objects/src/identity/sys-api-key-action-copy.test.ts @@ -23,6 +23,7 @@ import { zhCNObjects } from '../apps/translations/zh-CN.objects.generated.js'; import { jaJPObjects } from '../apps/translations/ja-JP.objects.generated.js'; import { esESObjects } from '../apps/translations/es-ES.objects.generated.js'; import { zhHantObjects } from '../apps/translations/zh-Hant.objects.generated.js'; +import { arObjects } from '../apps/translations/ar.objects.generated.js'; import { SysApiKey } from './sys-api-key.object.js'; interface CopyAction { @@ -40,6 +41,7 @@ const TRANSLATED = [ ['ja-JP', jaJPObjects], ['es-ES', esESObjects], ['zh-Hant', zhHantObjects], + ['ar', arObjects], ] as const; // `successMessage` is a `template` slot: the parsed object carries the diff --git a/packages/platform-objects/src/identity/transfer-ownership-action-copy.test.ts b/packages/platform-objects/src/identity/transfer-ownership-action-copy.test.ts index 2dea550d5ee..835901ea86d 100644 --- a/packages/platform-objects/src/identity/transfer-ownership-action-copy.test.ts +++ b/packages/platform-objects/src/identity/transfer-ownership-action-copy.test.ts @@ -22,6 +22,7 @@ import { zhCNObjects } from '../apps/translations/zh-CN.objects.generated.js'; import { jaJPObjects } from '../apps/translations/ja-JP.objects.generated.js'; import { esESObjects } from '../apps/translations/es-ES.objects.generated.js'; import { zhHantObjects } from '../apps/translations/zh-Hant.objects.generated.js'; +import { arObjects } from '../apps/translations/ar.objects.generated.js'; import { SysMember } from './sys-member.object.js'; // [#27948] An action's `description`, each param's `helpText` and each @@ -81,6 +82,7 @@ const LOCALES = [ ['ja-JP', jaJPObjects], ['es-ES', esESObjects], ['zh-Hant', zhHantObjects], + ['ar', arObjects], ] as const; describe('sys_member.transfer_ownership — declared copy', () => { diff --git a/packages/platform-objects/src/metadata-translations/index.ts b/packages/platform-objects/src/metadata-translations/index.ts index 0b37355a9cc..f4aa38ce7b5 100644 --- a/packages/platform-objects/src/metadata-translations/index.ts +++ b/packages/platform-objects/src/metadata-translations/index.ts @@ -6,10 +6,12 @@ import { zhCNMetadataForms } from '../apps/translations/zh-CN.metadata-forms.gen import { jaJPMetadataForms } from '../apps/translations/ja-JP.metadata-forms.generated.js'; import { esESMetadataForms } from '../apps/translations/es-ES.metadata-forms.generated.js'; import { zhHantMetadataForms } from '../apps/translations/zh-Hant.metadata-forms.generated.js'; +import { arMetadataForms } from '../apps/translations/ar.metadata-forms.generated.js'; import { zhCNGeneratedSourceHashes } from '../apps/translations/zh-CN.source-hashes.generated.js'; import { jaJPGeneratedSourceHashes } from '../apps/translations/ja-JP.source-hashes.generated.js'; import { esESGeneratedSourceHashes } from '../apps/translations/es-ES.source-hashes.generated.js'; import { zhHantGeneratedSourceHashes } from '../apps/translations/zh-Hant.source-hashes.generated.js'; +import { arGeneratedSourceHashes } from '../apps/translations/ar.source-hashes.generated.js'; import { withSourceFallback } from '../apps/translations/source-hash.js'; /** The source bundle the translated locales are judged against — `en` is a copy of it. */ @@ -46,4 +48,5 @@ export const MetadataFormsTranslations: TranslationBundle = { 'ja-JP': withSourceFallback({ metadataForms: jaJPMetadataForms }, enSource, undefined, jaJPGeneratedSourceHashes), 'es-ES': withSourceFallback({ metadataForms: esESMetadataForms }, enSource, undefined, esESGeneratedSourceHashes), 'zh-Hant': withSourceFallback({ metadataForms: zhHantMetadataForms }, enSource, undefined, zhHantGeneratedSourceHashes), + ar: withSourceFallback({ metadataForms: arMetadataForms }, enSource, undefined, arGeneratedSourceHashes), }; diff --git a/packages/platform-objects/src/pages/sys-organization.page.ts b/packages/platform-objects/src/pages/sys-organization.page.ts index 2a448edea13..df421c82e0f 100644 --- a/packages/platform-objects/src/pages/sys-organization.page.ts +++ b/packages/platform-objects/src/pages/sys-organization.page.ts @@ -80,7 +80,7 @@ export const SysOrganizationDetailPage: Page = { position: 'top', items: [ { - label: { en: 'Members', 'zh-CN': '成员', 'ja-JP': 'メンバー', 'es-ES': 'Miembros', 'zh-Hant': '成員' }, + label: { en: 'Members', 'zh-CN': '成员', 'ja-JP': 'メンバー', 'es-ES': 'Miembros', 'zh-Hant': '成員', ar: 'الأعضاء' }, icon: 'users', children: [ { @@ -92,13 +92,13 @@ export const SysOrganizationDetailPage: Page = { sort: [{ field: 'created_at', order: 'desc' }], limit: 25, showViewAll: true, - title: { en: 'Members', 'zh-CN': '成员', 'ja-JP': 'メンバー', 'es-ES': 'Miembros', 'zh-Hant': '成員' }, + title: { en: 'Members', 'zh-CN': '成员', 'ja-JP': 'メンバー', 'es-ES': 'Miembros', 'zh-Hant': '成員', ar: 'الأعضاء' }, }, }, ], }, { - label: { en: 'Invitations', 'zh-CN': '邀请', 'ja-JP': '招待', 'es-ES': 'Invitaciones', 'zh-Hant': '邀請' }, + label: { en: 'Invitations', 'zh-CN': '邀请', 'ja-JP': '招待', 'es-ES': 'Invitaciones', 'zh-Hant': '邀請', ar: 'الدعوات' }, icon: 'mail', children: [ { @@ -110,13 +110,13 @@ export const SysOrganizationDetailPage: Page = { sort: [{ field: 'created_at', order: 'desc' }], limit: 25, showViewAll: true, - title: { en: 'Invitations', 'zh-CN': '邀请', 'ja-JP': '招待', 'es-ES': 'Invitaciones', 'zh-Hant': '邀請' }, + title: { en: 'Invitations', 'zh-CN': '邀请', 'ja-JP': '招待', 'es-ES': 'Invitaciones', 'zh-Hant': '邀請', ar: 'الدعوات' }, }, }, ], }, { - label: { en: 'Teams', 'zh-CN': '团队', 'ja-JP': 'チーム', 'es-ES': 'Equipos', 'zh-Hant': '團隊' }, + label: { en: 'Teams', 'zh-CN': '团队', 'ja-JP': 'チーム', 'es-ES': 'Equipos', 'zh-Hant': '團隊', ar: 'الفرق' }, icon: 'users-round', children: [ { @@ -128,7 +128,7 @@ export const SysOrganizationDetailPage: Page = { sort: [{ field: 'name', order: 'asc' }], limit: 25, showViewAll: true, - title: { en: 'Teams', 'zh-CN': '团队', 'ja-JP': 'チーム', 'es-ES': 'Equipos', 'zh-Hant': '團隊' }, + title: { en: 'Teams', 'zh-CN': '团队', 'ja-JP': 'チーム', 'es-ES': 'Equipos', 'zh-Hant': '團隊', ar: 'الفرق' }, }, }, ], diff --git a/packages/platform-objects/src/pages/sys-user-ai-assistants-tab.test.ts b/packages/platform-objects/src/pages/sys-user-ai-assistants-tab.test.ts index 1b904d79fc1..2656464b215 100644 --- a/packages/platform-objects/src/pages/sys-user-ai-assistants-tab.test.ts +++ b/packages/platform-objects/src/pages/sys-user-ai-assistants-tab.test.ts @@ -98,6 +98,7 @@ describe('sys_user_detail — the AI Assistants tab', () => { 'ja-JP': 'AI アシスタント', 'es-ES': 'Asistentes de IA', 'zh-Hant': 'AI 助理', + ar: 'مساعدو الذكاء الاصطناعي', }); for (const sibling of items) { expect(Object.keys(sibling.label as Rec).sort(), String((sibling.label as Rec).en)).toEqual( diff --git a/packages/platform-objects/src/pages/sys-user-details-tab.test.ts b/packages/platform-objects/src/pages/sys-user-details-tab.test.ts index 22e9a28c8e5..a9274315b76 100644 --- a/packages/platform-objects/src/pages/sys-user-details-tab.test.ts +++ b/packages/platform-objects/src/pages/sys-user-details-tab.test.ts @@ -34,17 +34,17 @@ const HIDE_FIELDS = [ ]; const SECTIONS = [ { - label: { en: 'Identity', 'zh-CN': '身份', 'ja-JP': 'アイデンティティ', 'es-ES': 'Identidad', 'zh-Hant': '身分' }, + label: { en: 'Identity', 'zh-CN': '身份', 'ja-JP': 'アイデンティティ', 'es-ES': 'Identidad', 'zh-Hant': '身分', ar: 'الهوية' }, // #27859: a saved Locale is shown; the renderer draws only authored sections. fields: ['name', 'image', 'locale'], }, { // #27859: a banned user reads as banned, with the reason, not as active. - label: { en: 'Account Status', 'zh-CN': '账号状态', 'ja-JP': 'アカウントの状態', 'es-ES': 'Estado de la cuenta', 'zh-Hant': '帳號狀態' }, + label: { en: 'Account Status', 'zh-CN': '账号状态', 'ja-JP': 'アカウントの状態', 'es-ES': 'Estado de la cuenta', 'zh-Hant': '帳號狀態', ar: 'حالة الحساب' }, fields: ['banned', 'ban_reason', 'ban_expires'], }, { - label: { en: 'Audit', 'zh-CN': '审计', 'ja-JP': '監査', 'es-ES': 'Auditoría', 'zh-Hant': '稽核' }, + label: { en: 'Audit', 'zh-CN': '审计', 'ja-JP': '監査', 'es-ES': 'Auditoría', 'zh-Hant': '稽核', ar: 'التدقيق' }, fields: ['created_at', 'updated_at'], }, ]; diff --git a/packages/platform-objects/src/pages/sys-user.page.ts b/packages/platform-objects/src/pages/sys-user.page.ts index 261e56f805e..6ea01d3d1e0 100644 --- a/packages/platform-objects/src/pages/sys-user.page.ts +++ b/packages/platform-objects/src/pages/sys-user.page.ts @@ -142,6 +142,7 @@ export const SysUserDetailPage: Page = { 'ja-JP': 'メールが未認証です', 'es-ES': 'Correo no verificado', 'zh-Hant': '電子郵件尚未驗證', + ar: 'البريد الإلكتروني غير موثَّق', }, body: { en: 'Verify your email to receive password resets and important system notifications.', @@ -149,6 +150,7 @@ export const SysUserDetailPage: Page = { 'ja-JP': 'パスワードリセットや重要なシステム通知を受け取るには、メールアドレスを認証してください。', 'es-ES': 'Verifica tu correo para recibir restablecimientos de contraseña y notificaciones importantes del sistema.', 'zh-Hant': '驗證你的電子郵件,以接收密碼重設和重要的系統通知。', + ar: 'وثّق بريدك الإلكتروني لتتلقى رسائل إعادة تعيين كلمة المرور وإشعارات النظام المهمة.', }, dismissible: false, action: { @@ -159,6 +161,7 @@ export const SysUserDetailPage: Page = { 'ja-JP': '認証メールを再送信', 'es-ES': 'Reenviar correo de verificación', 'zh-Hant': '重新寄送驗證郵件', + ar: 'إعادة إرسال رسالة التوثيق', }, }, }, @@ -201,7 +204,7 @@ export const SysUserDetailPage: Page = { // authored sections, so a field none of them names is not on // the page: `locale` rides Identity, and the ban state has its // own section, so a banned user does not read as active. - label: { en: 'Details', 'zh-CN': '详情', 'ja-JP': '詳細', 'es-ES': 'Detalles', 'zh-Hant': '詳細資料' }, + label: { en: 'Details', 'zh-CN': '详情', 'ja-JP': '詳細', 'es-ES': 'Detalles', 'zh-Hant': '詳細資料', ar: 'التفاصيل' }, icon: 'file-text', children: [ { @@ -218,15 +221,15 @@ export const SysUserDetailPage: Page = { ], sections: [ { - label: { en: 'Identity', 'zh-CN': '身份', 'ja-JP': 'アイデンティティ', 'es-ES': 'Identidad', 'zh-Hant': '身分' }, + label: { en: 'Identity', 'zh-CN': '身份', 'ja-JP': 'アイデンティティ', 'es-ES': 'Identidad', 'zh-Hant': '身分', ar: 'الهوية' }, fields: ['name', 'image', 'locale'], }, { - label: { en: 'Account Status', 'zh-CN': '账号状态', 'ja-JP': 'アカウントの状態', 'es-ES': 'Estado de la cuenta', 'zh-Hant': '帳號狀態' }, + label: { en: 'Account Status', 'zh-CN': '账号状态', 'ja-JP': 'アカウントの状態', 'es-ES': 'Estado de la cuenta', 'zh-Hant': '帳號狀態', ar: 'حالة الحساب' }, fields: ['banned', 'ban_reason', 'ban_expires'], }, { - label: { en: 'Audit', 'zh-CN': '审计', 'ja-JP': '監査', 'es-ES': 'Auditoría', 'zh-Hant': '稽核' }, + label: { en: 'Audit', 'zh-CN': '审计', 'ja-JP': '監査', 'es-ES': 'Auditoría', 'zh-Hant': '稽核', ar: 'التدقيق' }, fields: ['created_at', 'updated_at'], }, ], @@ -235,7 +238,7 @@ export const SysUserDetailPage: Page = { ], }, { - label: { en: 'Positions', 'zh-CN': '岗位', 'ja-JP': 'ポジション', 'es-ES': 'Puestos', 'zh-Hant': '職位' }, + label: { en: 'Positions', 'zh-CN': '岗位', 'ja-JP': 'ポジション', 'es-ES': 'Puestos', 'zh-Hant': '職位', ar: 'المناصب' }, icon: 'shield-check', children: [ { @@ -255,7 +258,7 @@ export const SysUserDetailPage: Page = { sort: [{ field: 'created_at', order: 'desc' }], limit: 25, showViewAll: true, - title: { en: 'Positions', 'zh-CN': '岗位', 'ja-JP': 'ポジション', 'es-ES': 'Puestos', 'zh-Hant': '職位' }, + title: { en: 'Positions', 'zh-CN': '岗位', 'ja-JP': 'ポジション', 'es-ES': 'Puestos', 'zh-Hant': '職位', ar: 'المناصب' }, }, }, ], @@ -278,7 +281,7 @@ export const SysUserDetailPage: Page = { // a row showing only the set would read as a live grant when it is // not. The same form sets the window, and the object refuses a // window whose end is not after its start. - label: { en: 'Permission Sets', 'zh-CN': '权限集', 'ja-JP': '権限セット', 'es-ES': 'Conjuntos de permisos', 'zh-Hant': '權限集' }, + label: { en: 'Permission Sets', 'zh-CN': '权限集', 'ja-JP': '権限セット', 'es-ES': 'Conjuntos de permisos', 'zh-Hant': '權限集', ar: 'مجموعات الأذونات' }, icon: 'lock', children: [ { @@ -290,7 +293,7 @@ export const SysUserDetailPage: Page = { sort: [{ field: 'created_at', order: 'desc' }], limit: 25, showViewAll: true, - title: { en: 'Permission Sets', 'zh-CN': '权限集', 'ja-JP': '権限セット', 'es-ES': 'Conjuntos de permisos', 'zh-Hant': '權限集' }, + title: { en: 'Permission Sets', 'zh-CN': '权限集', 'ja-JP': '権限セット', 'es-ES': 'Conjuntos de permisos', 'zh-Hant': '權限集', ar: 'مجموعات الأذونات' }, }, }, ], @@ -303,7 +306,7 @@ export const SysUserDetailPage: Page = { // The Add picker binds a BU by its id (linkField: 'business_unit_id'); // sys_business_unit has no `label` field so the picker labels rows by // its display field `name`. - label: { en: 'Business Units', 'zh-CN': '业务单元', 'ja-JP': 'ビジネスユニット', 'es-ES': 'Unidades de negocio', 'zh-Hant': '業務單位' }, + label: { en: 'Business Units', 'zh-CN': '业务单元', 'ja-JP': 'ビジネスユニット', 'es-ES': 'Unidades de negocio', 'zh-Hant': '業務單位', ar: 'وحدات الأعمال' }, icon: 'network', children: [ { @@ -315,7 +318,7 @@ export const SysUserDetailPage: Page = { sort: [{ field: 'created_at', order: 'desc' }], limit: 25, showViewAll: true, - title: { en: 'Business Units', 'zh-CN': '业务单元', 'ja-JP': 'ビジネスユニット', 'es-ES': 'Unidades de negocio', 'zh-Hant': '業務單位' }, + title: { en: 'Business Units', 'zh-CN': '业务单元', 'ja-JP': 'ビジネスユニット', 'es-ES': 'Unidades de negocio', 'zh-Hant': '業務單位', ar: 'وحدات الأعمال' }, add: { picker: { object: 'sys_business_unit', @@ -328,6 +331,7 @@ export const SysUserDetailPage: Page = { 'ja-JP': 'ビジネスユニットに追加', 'es-ES': 'Añadir a unidad de negocio', 'zh-Hant': '加入業務單位', + ar: 'إضافة إلى وحدة أعمال', }, }, }, @@ -335,7 +339,7 @@ export const SysUserDetailPage: Page = { ], }, { - label: { en: 'Sessions', 'zh-CN': '会话', 'ja-JP': 'セッション', 'es-ES': 'Sesiones', 'zh-Hant': '工作階段' }, + label: { en: 'Sessions', 'zh-CN': '会话', 'ja-JP': 'セッション', 'es-ES': 'Sesiones', 'zh-Hant': '工作階段', ar: 'الجلسات' }, icon: 'monitor', children: [ { @@ -347,13 +351,13 @@ export const SysUserDetailPage: Page = { sort: [{ field: 'created_at', order: 'desc' }], limit: 25, showViewAll: true, - title: { en: 'Sessions', 'zh-CN': '会话', 'ja-JP': 'セッション', 'es-ES': 'Sesiones', 'zh-Hant': '工作階段' }, + title: { en: 'Sessions', 'zh-CN': '会话', 'ja-JP': 'セッション', 'es-ES': 'Sesiones', 'zh-Hant': '工作階段', ar: 'الجلسات' }, }, }, ], }, { - label: { en: 'Linked Accounts', 'zh-CN': '关联账号', 'ja-JP': '連携アカウント', 'es-ES': 'Cuentas vinculadas', 'zh-Hant': '已關聯帳戶' }, + label: { en: 'Linked Accounts', 'zh-CN': '关联账号', 'ja-JP': '連携アカウント', 'es-ES': 'Cuentas vinculadas', 'zh-Hant': '已關聯帳戶', ar: 'الحسابات المرتبطة' }, icon: 'link', children: [ { @@ -365,13 +369,13 @@ export const SysUserDetailPage: Page = { sort: [{ field: 'created_at', order: 'desc' }], limit: 25, showViewAll: true, - title: { en: 'Linked Accounts', 'zh-CN': '关联账号', 'ja-JP': '連携アカウント', 'es-ES': 'Cuentas vinculadas', 'zh-Hant': '已關聯帳戶' }, + title: { en: 'Linked Accounts', 'zh-CN': '关联账号', 'ja-JP': '連携アカウント', 'es-ES': 'Cuentas vinculadas', 'zh-Hant': '已關聯帳戶', ar: 'الحسابات المرتبطة' }, }, }, ], }, { - label: { en: 'Organizations', 'zh-CN': '组织', 'ja-JP': '組織', 'es-ES': 'Organizaciones', 'zh-Hant': '組織' }, + label: { en: 'Organizations', 'zh-CN': '组织', 'ja-JP': '組織', 'es-ES': 'Organizaciones', 'zh-Hant': '組織', ar: 'المؤسسات' }, icon: 'building-2', children: [ { @@ -383,13 +387,13 @@ export const SysUserDetailPage: Page = { sort: [{ field: 'created_at', order: 'desc' }], limit: 25, showViewAll: true, - title: { en: 'Organizations', 'zh-CN': '组织', 'ja-JP': '組織', 'es-ES': 'Organizaciones', 'zh-Hant': '組織' }, + title: { en: 'Organizations', 'zh-CN': '组织', 'ja-JP': '組織', 'es-ES': 'Organizaciones', 'zh-Hant': '組織', ar: 'المؤسسات' }, }, }, ], }, { - label: { en: 'OAuth Apps', 'zh-CN': 'OAuth 应用', 'ja-JP': 'OAuth アプリ', 'es-ES': 'Aplicaciones OAuth', 'zh-Hant': 'OAuth 應用程式' }, + label: { en: 'OAuth Apps', 'zh-CN': 'OAuth 应用', 'ja-JP': 'OAuth アプリ', 'es-ES': 'Aplicaciones OAuth', 'zh-Hant': 'OAuth 應用程式', ar: 'تطبيقات OAuth' }, icon: 'key-square', children: [ { @@ -401,7 +405,7 @@ export const SysUserDetailPage: Page = { sort: [{ field: 'created_at', order: 'desc' }], limit: 25, showViewAll: true, - title: { en: 'OAuth Apps', 'zh-CN': 'OAuth 应用', 'ja-JP': 'OAuth アプリ', 'es-ES': 'Aplicaciones OAuth', 'zh-Hant': 'OAuth 應用程式' }, + title: { en: 'OAuth Apps', 'zh-CN': 'OAuth 应用', 'ja-JP': 'OAuth アプリ', 'es-ES': 'Aplicaciones OAuth', 'zh-Hant': 'OAuth 應用程式', ar: 'تطبيقات OAuth' }, }, }, ], @@ -426,7 +430,7 @@ export const SysUserDetailPage: Page = { // reasons: this raw `Page` literal reaches the wire verbatim, and // only the `{ dialect: 'cel' }` envelope routes to CEL. { - label: { en: 'Security', 'zh-CN': '安全', 'ja-JP': 'セキュリティ', 'es-ES': 'Seguridad', 'zh-Hant': '安全' }, + label: { en: 'Security', 'zh-CN': '安全', 'ja-JP': 'セキュリティ', 'es-ES': 'Seguridad', 'zh-Hant': '安全', ar: 'الأمان' }, icon: 'shield', visibleWhen: P`has(record.id) && record.id == ctx.user.id`, children: [ @@ -440,6 +444,7 @@ export const SysUserDetailPage: Page = { 'ja-JP': 'パスワードとサインイン', 'es-ES': 'Contraseña e inicio de sesión', 'zh-Hant': '密碼與登入', + ar: 'كلمة المرور وتسجيل الدخول', }, }, }, @@ -453,6 +458,7 @@ export const SysUserDetailPage: Page = { 'ja-JP': 'パスワード、またはこのアカウントに関連付けられたメールアドレスを変更します。', 'es-ES': 'Cambia tu contraseña o la dirección de correo asociada a esta cuenta.', 'zh-Hant': '變更密碼或此帳號繫結的電子郵件地址。', + ar: 'غيّر كلمة المرور أو عنوان البريد الإلكتروني المرتبط بهذا الحساب.', }, }, }, @@ -481,6 +487,7 @@ export const SysUserDetailPage: Page = { 'ja-JP': '二要素認証', 'es-ES': 'Autenticación de dos factores', 'zh-Hant': '兩步驟驗證', + ar: 'المصادقة الثنائية', }, }, }, @@ -495,6 +502,7 @@ export const SysUserDetailPage: Page = { 'ja-JP': 'TOTP 認証アプリでセキュリティをもう一段強化します。バックアップコードがあれば、デバイスを紛失してもサインインできます。', 'es-ES': 'Añade una segunda capa de seguridad con una app de autenticación TOTP. Los códigos de respaldo te permiten iniciar sesión si pierdes tu dispositivo.', 'zh-Hant': '使用 TOTP 驗證器應用程式新增第二層安全防護。裝置遺失時,可使用備用驗證碼登入。', + ar: 'أضف طبقة أمان ثانية باستخدام تطبيق مصادقة TOTP. تتيح لك الرموز الاحتياطية تسجيل الدخول إذا فقدت جهازك.', }, }, }, @@ -527,6 +535,7 @@ export const SysUserDetailPage: Page = { 'ja-JP': 'メール認証', 'es-ES': 'Verificación de correo', 'zh-Hant': '電子郵件驗證', + ar: 'توثيق البريد الإلكتروني', }, }, }, @@ -540,6 +549,7 @@ export const SysUserDetailPage: Page = { 'ja-JP': 'パスワードリセットや通知を受け取れるよう、メールアドレスを認証してください。ボタンは未認証の間のみ表示されます。', 'es-ES': 'Verifica tu correo para recibir restablecimientos de contraseña y notificaciones. El botón solo aparece mientras la verificación está pendiente.', 'zh-Hant': '驗證你的電子郵件,以便接收密碼重設和系統通知。此按鈕僅在電子郵件待驗證時顯示。', + ar: 'وثّق بريدك الإلكتروني لتصلك رسائل إعادة تعيين كلمة المرور والإشعارات. لا يظهر الزر إلا ما دام التوثيق معلّقًا.', }, }, }, @@ -562,6 +572,7 @@ export const SysUserDetailPage: Page = { 'ja-JP': '危険な操作', 'es-ES': 'Zona de peligro', 'zh-Hant': '危險操作', + ar: 'منطقة الخطر', }, }, className: 'text-destructive', @@ -576,6 +587,7 @@ export const SysUserDetailPage: Page = { 'ja-JP': 'この操作は取り消せません。削除されたアカウントは復元できません。', 'es-ES': 'Permanente. Una vez eliminada, tu cuenta no se puede recuperar.', 'zh-Hant': '此動作無法復原,帳號一經刪除即無法恢復。', + ar: 'إجراء نهائي. بمجرد حذف حسابك، لا يمكن استعادته.', }, }, }, @@ -594,7 +606,7 @@ export const SysUserDetailPage: Page = { // user_id FK; the sys_api_key object's own list-item actions // (revoke / restore) handle row operations. { - label: { en: 'API Keys', 'zh-CN': 'API 密钥', 'ja-JP': 'API キー', 'es-ES': 'Claves de API', 'zh-Hant': 'API 金鑰' }, + label: { en: 'API Keys', 'zh-CN': 'API 密钥', 'ja-JP': 'API キー', 'es-ES': 'Claves de API', 'zh-Hant': 'API 金鑰', ar: 'مفاتيح API' }, icon: 'key-round', children: [ { @@ -606,7 +618,7 @@ export const SysUserDetailPage: Page = { sort: [{ field: 'created_at', order: 'desc' }], limit: 25, showViewAll: true, - title: { en: 'API Keys', 'zh-CN': 'API 密钥', 'ja-JP': 'API キー', 'es-ES': 'Claves de API', 'zh-Hant': 'API 金鑰' }, + title: { en: 'API Keys', 'zh-CN': 'API 密钥', 'ja-JP': 'API キー', 'es-ES': 'Claves de API', 'zh-Hant': 'API 金鑰', ar: 'مفاتيح API' }, }, }, ], @@ -635,7 +647,7 @@ export const SysUserDetailPage: Page = { // anyone else either way. Same `P` envelope as the Security tab: // this raw `Page` literal reaches the wire verbatim. { - label: { en: 'AI Assistants', 'zh-CN': 'AI 助手', 'ja-JP': 'AI アシスタント', 'es-ES': 'Asistentes de IA', 'zh-Hant': 'AI 助理' }, + label: { en: 'AI Assistants', 'zh-CN': 'AI 助手', 'ja-JP': 'AI アシスタント', 'es-ES': 'Asistentes de IA', 'zh-Hant': 'AI 助理', ar: 'مساعدو الذكاء الاصطناعي' }, icon: 'bot', visibleWhen: P`current_user.isPlatformAdmin == true`, children: [ @@ -656,7 +668,7 @@ export const SysUserDetailPage: Page = { // object's `managedBy: 'system-data'` and the caller's grant, the // same verdict the grid's buttons read. { - label: { en: 'Preferences', 'zh-CN': '偏好设置', 'ja-JP': '設定', 'es-ES': 'Preferencias', 'zh-Hant': '偏好設定' }, + label: { en: 'Preferences', 'zh-CN': '偏好设置', 'ja-JP': '設定', 'es-ES': 'Preferencias', 'zh-Hant': '偏好設定', ar: 'التفضيلات' }, icon: 'sliders', children: [ { @@ -668,7 +680,7 @@ export const SysUserDetailPage: Page = { sort: [{ field: 'updated_at', order: 'desc' }], limit: 25, showViewAll: true, - title: { en: 'Preferences', 'zh-CN': '偏好设置', 'ja-JP': '設定', 'es-ES': 'Preferencias', 'zh-Hant': '偏好設定' }, + title: { en: 'Preferences', 'zh-CN': '偏好设置', 'ja-JP': '設定', 'es-ES': 'Preferencias', 'zh-Hant': '偏好設定', ar: 'التفضيلات' }, }, }, ], diff --git a/packages/platform-objects/src/plugin.translations.test.ts b/packages/platform-objects/src/plugin.translations.test.ts index 96a23daea80..37ec2afb8f3 100644 --- a/packages/platform-objects/src/plugin.translations.test.ts +++ b/packages/platform-objects/src/plugin.translations.test.ts @@ -191,8 +191,8 @@ describe('PlatformObjectsPlugin: a host translation bundle wins over the platfor await boot(ctx); - // Two bundles (Setup, MetadataForms) times the five locales each ships. - expect(loads.sort()).toEqual(['en', 'en', 'es-ES', 'es-ES', 'ja-JP', 'ja-JP', 'zh-CN', 'zh-CN', 'zh-Hant', 'zh-Hant']); + // Two bundles (Setup, MetadataForms) times the six locales each ships. + expect(loads.sort()).toEqual(['ar', 'ar', 'en', 'en', 'es-ES', 'es-ES', 'ja-JP', 'ja-JP', 'zh-CN', 'zh-CN', 'zh-Hant', 'zh-Hant']); }); it('fail-safe: a provider with no getTranslations still gets the platform bundles, and says so', async () => { diff --git a/packages/plugins/plugin-approvals/src/translations/approval-status-vocabulary.test.ts b/packages/plugins/plugin-approvals/src/translations/approval-status-vocabulary.test.ts index f9860bf3f3e..c43e8cf99aa 100644 --- a/packages/plugins/plugin-approvals/src/translations/approval-status-vocabulary.test.ts +++ b/packages/plugins/plugin-approvals/src/translations/approval-status-vocabulary.test.ts @@ -41,7 +41,7 @@ import { describe, it, expect } from 'vitest'; import { APPROVAL_STATUSES, APPROVAL_STATUS_LABELS } from '@objectstack/spec/contracts'; import { translateObject, resolveViewLabel } from '@objectstack/spec/system'; import type { TranslationData } from '@objectstack/spec/system'; -import { zhCN, jaJP, esES, zhHant } from '@objectstack/platform-objects/apps'; +import { zhCN, jaJP, esES, zhHant, ar } from '@objectstack/platform-objects/apps'; import { ApprovalsTranslations } from './index.js'; import { SysApprovalRequest } from '../sys-approval-request.object.js'; @@ -141,6 +141,7 @@ describe('approval status vocabulary (#7232)', () => { ['ja-JP', jaJP], ['es-ES', esES], ['zh-Hant', zhHant], + ['ar', ar], ] as const) { const navLabel = navApprovalsLabel(nav); expect(navLabel, `platform-objects lost the ${locale} nav_account_approvals label`).toBeTruthy(); diff --git a/packages/plugins/plugin-security/src/people-org-assignment-nav-contribution.test.ts b/packages/plugins/plugin-security/src/people-org-assignment-nav-contribution.test.ts index 495230e8018..2ef1411a060 100644 --- a/packages/plugins/plugin-security/src/people-org-assignment-nav-contribution.test.ts +++ b/packages/plugins/plugin-security/src/people-org-assignment-nav-contribution.test.ts @@ -180,7 +180,7 @@ describe('SecurityPlugin — People & Organization assignment entries are labell ((SetupAppTranslations as any)[locale]?.apps?.setup?.navigation ?? {}) as Record; const locales = Object.keys(SetupAppTranslations).filter((l) => navOf(l).group_people_org?.label); // Control: every Setup locale carries the group, so the loop below judges something. - expect(locales.sort()).toEqual(['en', 'es-ES', 'ja-JP', 'zh-CN', 'zh-Hant']); + expect(locales.sort()).toEqual(['ar', 'en', 'es-ES', 'ja-JP', 'zh-CN', 'zh-Hant']); const en = navOf('en'); expect(en[POSITION_HOLDERS.id]?.label).toBe(POSITION_HOLDERS.label); diff --git a/packages/services/service-settings/src/setup-nav-hub-parity.test.ts b/packages/services/service-settings/src/setup-nav-hub-parity.test.ts index 1331014d50c..a778159972d 100644 --- a/packages/services/service-settings/src/setup-nav-hub-parity.test.ts +++ b/packages/services/service-settings/src/setup-nav-hub-parity.test.ts @@ -46,7 +46,7 @@ import { settingsBuiltinTranslations } from './translations/index.js'; /** Where the hub opens a namespace; the sidebar entry for it points here. */ const SETTINGS_PAGE = /^\/apps\/setup\/system\/settings\/([a-z_]+)$/; -const TRANSLATED_LOCALES = ['zh-CN', 'ja-JP', 'es-ES', 'zh-Hant'] as const; +const TRANSLATED_LOCALES = ['zh-CN', 'ja-JP', 'es-ES', 'zh-Hant', 'ar'] as const; interface NavItem { id?: string; type?: string; label?: string; url?: string; children?: NavItem[] }