Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .changeset/30512-plugin-security-locales.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@objectstack/plugin-security': patch
---

i18n(plugin-security): the security objects, built-in permission sets and built-in positions ship in all eleven platform languages

Clause-②: no
Metadata-protocol: none

The security objects, the names and descriptions of the built-in permission sets and the names of the built-in positions now carry `zh-Hant`, `ar`, `fr`, `pt-BR`, `ru`, `de` and `ko` beside the existing `en`, `zh-CN`, `ja-JP` and `es-ES`, so an administrator in one of those languages reads the security screens in their own language instead of English. Each new locale is registered under its platform-table tag with its source-hash companion, the hand-authored permission-set and position tables carry an entry in every locale, and the package's extract command lists the tags, so the existing `check:i18n` bundle check covers them. Package 7 of #30512.
16 changes: 15 additions & 1 deletion packages/plugins/plugin-security/scripts/i18n-extract.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* strings were seeded from @objectstack/platform-objects.)
*
* os i18n extract packages/plugins/plugin-security/scripts/i18n-extract.config.ts \
* --locales=zh-CN,ja-JP,es-ES --fill=default --objects-only --no-metadata-forms \
* --locales=zh-CN,ja-JP,es-ES,zh-Hant,ar,fr,pt-BR,ru,de,ko --fill=default --objects-only --no-metadata-forms \
* --source-hashes \
* --out=packages/plugins/plugin-security/src/translations
*
Expand Down Expand Up @@ -47,6 +47,13 @@ import { enObjects } from '../src/translations/en.objects.generated.js';
import { zhCNObjects } from '../src/translations/zh-CN.objects.generated.js';
import { jaJPObjects } from '../src/translations/ja-JP.objects.generated.js';
import { esESObjects } from '../src/translations/es-ES.objects.generated.js';
import { zhHantObjects } from '../src/translations/zh-Hant.objects.generated.js';
import { arObjects } from '../src/translations/ar.objects.generated.js';
import { frObjects } from '../src/translations/fr.objects.generated.js';
import { ptBRObjects } from '../src/translations/pt-BR.objects.generated.js';
import { ruObjects } from '../src/translations/ru.objects.generated.js';
import { deObjects } from '../src/translations/de.objects.generated.js';
import { koObjects } from '../src/translations/ko.objects.generated.js';

/**
* The annotation is load-bearing, not decoration (#10868). `defineStack`
Expand All @@ -69,6 +76,13 @@ const config: ObjectStackDefinition = defineStack({
{ 'zh-CN': { objects: zhCNObjects } },
{ 'ja-JP': { objects: jaJPObjects } },
{ 'es-ES': { objects: esESObjects } },
{ 'zh-Hant': { objects: zhHantObjects } },
{ 'ar': { objects: arObjects } },
{ 'fr': { objects: frObjects } },
{ 'pt-BR': { objects: ptBRObjects } },
{ 'ru': { objects: ruObjects } },
{ 'de': { objects: deObjects } },
{ 'ko': { objects: koObjects } },
],
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
// 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<TranslationData['objects']> = {
sys_user_permission_set: {
label: "مجموعة أذونات المستخدم",
pluralLabel: "مجموعات أذونات المستخدمين",
description: "إسناد مباشر لمجموعة أذونات إلى مستخدم (ويمكن حصره في مؤسسة).",
fields: {
id: {
label: "معرّف الإسناد",
help: "معرّف UUID للإسناد."
},
display_title: {
label: "العنوان",
help: "عنوان السجل: المستخدم ومجموعة الأذونات المسندة إليه (يُحسب عند القراءة)"
},
user_id: {
label: "المستخدم",
help: "المستخدم الذي يتلقى مجموعة الأذونات هذه."
},
permission_set: {
label: "مجموعة الأذونات",
help: "مجموعة الأذونات التي يمنحها هذا الإسناد للمستخدم. اختر إحدى مجموعات الأذونات المعرّفة في الإعداد؛ ويُرفض الاسم الذي لا يطابق أي مجموعة."
},
organization_id: {
label: "المؤسسة",
help: "المؤسسة التي ينتمي إليها هذا المنح."
},
granted_by: {
label: "مَن منحه",
help: "المستخدم الذي منح مجموعة الأذونات هذه."
},
valid_from: {
label: "صالح من",
help: "يسري الإسناد في هذا الوقت. اتركه فارغًا ليسري فورًا. يبدأ في موعده دون انتظار أي مهمة مجدولة."
},
valid_until: {
label: "صالح حتى",
help: "ينتهي الإسناد في هذا الوقت (UTC). اتركه فارغًا لعدم تحديد تاريخ انتهاء. مطلوب في الوصول الطارئ وفي الإسناد إلى وكيل ذكاء اصطناعي. ينتهي في موعده دون انتظار أي مهمة مجدولة."
},
reason: {
label: "السبب",
help: "سبب وجود هذا الإسناد. مطلوب في الإسنادات المفوَّضة وإسنادات الوصول الطارئ. بالنسبة إلى وكيل ذكاء اصطناعي، اذكر المهمة التي مُنح الوصول من أجلها."
},
last_certified_at: {
label: "آخر اعتماد في",
help: "غير مستخدم بعد: محجوز لميزة مستقبلية لمراجعة الوصول. لا شيء يملأ هذا التاريخ أو يتحقق منه، لذا فالقيمة الفارغة لا تعني أن الإسناد لم يُراجَع."
},
certified_by: {
label: "مَن اعتمده",
help: "غير مستخدم بعد: محجوز لميزة مستقبلية لمراجعة الوصول. المراجِع المُدخَل هنا لا يُتحقق منه ولا يغيّر شيئًا مما يمنحه الإسناد."
},
created_at: {
label: "تاريخ الإنشاء"
},
updated_at: {
label: "تاريخ التحديث"
}
},
_validations: {
validity_window_order: {
message: "يجب أن يكون «صالح حتى» بعد «صالح من». يسري المنح من «صالح من» حتى «صالح حتى» دون أن يشمله، لذا فالنافذة التي تنتهي عند بدايتها أو قبلها لا تمنح شيئًا. اترك أيًّا منهما فارغًا لنافذة مفتوحة."
}
}
},
sys_user_position: {
label: "منصب المستخدم",
pluralLabel: "مناصب المستخدمين",
description: "المناصب التي يشغلها كل مستخدم، صف واحد لكل إسناد.",
fields: {
id: {
label: "معرّف الإسناد",
help: "معرّف UUID لإسناد المستخدم إلى المنصب."
},
display_title: {
label: "العنوان",
help: "عنوان السجل: المستخدم والمنصب الذي يشغله (يُحسب عند القراءة)"
},
user_id: {
label: "المستخدم",
help: "المستخدم الذي يشغل هذا المنصب."
},
position: {
label: "المنصب",
help: "الاسم الآلي للمنصب، ويُحلّ في كتالوج الأمان."
},
business_unit_id: {
label: "وحدة الأعمال",
help: "مرساة وحدة الأعمال على مستوى الإسناد: حيث ينطبق إسناد هذا المنصب. مرساة العمق لـ readScope/writeScope، وحدّ الإدارة المفوَّضة، وحقيقة تدقيق. القيمة الفارغة = غير مرسَى (قديم / على مستوى المستأجر)؛ ويجب على المسؤولين المفوَّضين إرساء الإسنادات داخل شجرتهم الفرعية."
},
organization_id: {
label: "المؤسسة",
help: "المؤسسة التي ينتمي إليها هذا الإسناد."
},
granted_by: {
label: "مَن منحه",
help: "المستخدم الذي منح إسناد هذا المنصب (يختمه حارس الإدارة المفوَّضة عند الكتابة المفوَّضة)."
},
valid_from: {
label: "صالح من",
help: "يسري الإسناد في هذا الوقت. اتركه فارغًا ليسري فورًا. يبدأ في موعده دون انتظار أي مهمة مجدولة."
},
valid_until: {
label: "صالح حتى",
help: "ينتهي الإسناد في هذا الوقت (UTC). اتركه فارغًا لعدم تحديد تاريخ انتهاء. مطلوب في الإسناد المفوَّض. ينتهي في موعده دون انتظار أي مهمة مجدولة."
},
reason: {
label: "السبب",
help: "سبب وجود هذا المنح. نص حر؛ مطلوب في صفوف التفويض والوصول الطارئ (كسر الزجاج)."
},
delegated_from: {
label: "مفوَّض من",
help: "المفوِّض الذي يحمل هذا الصف صلاحيته (职务代理). الصف الذي ضُبط فيه delegated_from لا يمكن تفويضه بدوره ولا تجديده ذاتيًا — تُقطع السلاسل في الاتجاهين."
},
last_certified_at: {
label: "آخر اعتماد في",
help: "غير مستخدم بعد: محجوز لميزة مستقبلية لمراجعة الوصول. لا شيء يملأ هذا التاريخ أو يتحقق منه، لذا فالقيمة الفارغة لا تعني أن الإسناد لم يُراجَع."
},
certified_by: {
label: "مَن اعتمده",
help: "غير مستخدم بعد: محجوز لميزة مستقبلية لمراجعة الوصول. المراجِع المُدخَل هنا لا يُتحقق منه ولا يغيّر شيئًا مما يمنحه الإسناد."
},
created_at: {
label: "تاريخ الإنشاء"
},
updated_at: {
label: "تاريخ التحديث"
}
},
_validations: {
reserved_identity_position: {
message: "لا يمكن أن يكون «position» اسم هوية مدمجة محجوزًا للإطار (platform_admin، org_owner، org_admin، org_member، org_external). هذه الأسماء هويات مدمجة: تُسقطها المنصة في current_user.positions من مصادرها الموثوقة، والصف الذي يحمل أحدها ليس إسنادًا لتلك الهوية. اختر اسمًا آخر."
}
}
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// 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
* `<locale>.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<Record<string, string>> = {
};
Loading
Loading