create-mithaq 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/LICENSES/openstatus-time-picker.txt +21 -0
- package/LICENSES/payload.txt +22 -0
- package/LICENSES/ponytail.txt +21 -0
- package/index.js +521 -0
- package/package.json +19 -4
- package/template/apps/asas-backoffice/components.json +25 -0
- package/template/apps/asas-backoffice/gitignore +3 -0
- package/template/apps/asas-backoffice/package.json +57 -0
- package/template/apps/asas-backoffice/public/favicon.svg +1 -0
- package/template/apps/asas-backoffice/src/api.ts +147 -0
- package/template/apps/asas-backoffice/src/auth-frame.tsx +150 -0
- package/template/apps/asas-backoffice/src/components/ui/accordion.tsx +74 -0
- package/template/apps/asas-backoffice/src/components/ui/alert-dialog.tsx +185 -0
- package/template/apps/asas-backoffice/src/components/ui/avatar.tsx +106 -0
- package/template/apps/asas-backoffice/src/components/ui/badge.tsx +48 -0
- package/template/apps/asas-backoffice/src/components/ui/breadcrumb.tsx +124 -0
- package/template/apps/asas-backoffice/src/components/ui/button-group.tsx +87 -0
- package/template/apps/asas-backoffice/src/components/ui/button.tsx +55 -0
- package/template/apps/asas-backoffice/src/components/ui/calendar.tsx +223 -0
- package/template/apps/asas-backoffice/src/components/ui/card.tsx +102 -0
- package/template/apps/asas-backoffice/src/components/ui/chart.tsx +371 -0
- package/template/apps/asas-backoffice/src/components/ui/checkbox.tsx +28 -0
- package/template/apps/asas-backoffice/src/components/ui/collapsible.tsx +19 -0
- package/template/apps/asas-backoffice/src/components/ui/combobox.tsx +308 -0
- package/template/apps/asas-backoffice/src/components/ui/dialog.tsx +158 -0
- package/template/apps/asas-backoffice/src/components/ui/direction.tsx +6 -0
- package/template/apps/asas-backoffice/src/components/ui/directional-icon.tsx +20 -0
- package/template/apps/asas-backoffice/src/components/ui/dropdown-menu.tsx +267 -0
- package/template/apps/asas-backoffice/src/components/ui/field.tsx +236 -0
- package/template/apps/asas-backoffice/src/components/ui/input-group.tsx +155 -0
- package/template/apps/asas-backoffice/src/components/ui/input-otp.tsx +84 -0
- package/template/apps/asas-backoffice/src/components/ui/input.tsx +19 -0
- package/template/apps/asas-backoffice/src/components/ui/label.tsx +19 -0
- package/template/apps/asas-backoffice/src/components/ui/popover.tsx +90 -0
- package/template/apps/asas-backoffice/src/components/ui/radio-group.tsx +37 -0
- package/template/apps/asas-backoffice/src/components/ui/resizable.tsx +47 -0
- package/template/apps/asas-backoffice/src/components/ui/scroll-area.tsx +54 -0
- package/template/apps/asas-backoffice/src/components/ui/select.tsx +204 -0
- package/template/apps/asas-backoffice/src/components/ui/separator.tsx +24 -0
- package/template/apps/asas-backoffice/src/components/ui/sheet.tsx +139 -0
- package/template/apps/asas-backoffice/src/components/ui/sidebar.tsx +714 -0
- package/template/apps/asas-backoffice/src/components/ui/skeleton.tsx +13 -0
- package/template/apps/asas-backoffice/src/components/ui/slider.tsx +55 -0
- package/template/apps/asas-backoffice/src/components/ui/spinner.tsx +10 -0
- package/template/apps/asas-backoffice/src/components/ui/switch.tsx +29 -0
- package/template/apps/asas-backoffice/src/components/ui/table.tsx +115 -0
- package/template/apps/asas-backoffice/src/components/ui/tabs.tsx +79 -0
- package/template/apps/asas-backoffice/src/components/ui/textarea.tsx +17 -0
- package/template/apps/asas-backoffice/src/components/ui/toggle-group.tsx +89 -0
- package/template/apps/asas-backoffice/src/components/ui/toggle.tsx +44 -0
- package/template/apps/asas-backoffice/src/components/ui/tooltip.tsx +63 -0
- package/template/apps/asas-backoffice/src/components.ts +35 -0
- package/template/apps/asas-backoffice/src/copy.ts +1 -0
- package/template/apps/asas-backoffice/src/first-user.ts +14 -0
- package/template/apps/asas-backoffice/src/hooks/use-mobile.ts +19 -0
- package/template/apps/asas-backoffice/src/lib/utils.ts +1 -0
- package/template/apps/asas-backoffice/src/links.ts +38 -0
- package/template/apps/asas-backoffice/src/locale-switch.tsx +28 -0
- package/template/apps/asas-backoffice/src/no-organization.tsx +5 -0
- package/template/apps/asas-backoffice/src/redirect.ts +1 -0
- package/template/apps/asas-backoffice/src/request-headers.ts +11 -0
- package/template/apps/asas-backoffice/src/routeTree.gen.ts +206 -0
- package/template/apps/asas-backoffice/src/router.tsx +18 -0
- package/template/apps/asas-backoffice/src/routes/(screens)/backoffice/files.ts +10 -0
- package/template/apps/asas-backoffice/src/routes/(screens)/backoffice/screen.json +3971 -0
- package/template/apps/asas-backoffice/src/routes/(screens)/backoffice/types.ts +163 -0
- package/template/apps/asas-backoffice/src/routes/(screens)/backoffice.$.tsx +17 -0
- package/template/apps/asas-backoffice/src/routes/(screens)/backoffice.index.tsx +10 -0
- package/template/apps/asas-backoffice/src/routes/(screens)/backoffice.tsx +13 -0
- package/template/apps/asas-backoffice/src/routes/__root.tsx +95 -0
- package/template/apps/asas-backoffice/src/routes/api/$.ts +34 -0
- package/template/apps/asas-backoffice/src/routes/auth.create-first-user.tsx +113 -0
- package/template/apps/asas-backoffice/src/routes/auth.sign-in.tsx +77 -0
- package/template/apps/asas-backoffice/src/routes/unauthorized.tsx +134 -0
- package/template/apps/asas-backoffice/src/screen-form.tsx +36 -0
- package/template/apps/asas-backoffice/src/screen-list.tsx +140 -0
- package/template/apps/asas-backoffice/src/screen-names.ts +10 -0
- package/template/apps/asas-backoffice/src/screens.tsx +888 -0
- package/template/apps/asas-backoffice/src/server.ts +30 -0
- package/template/apps/asas-backoffice/src/slide.ts +22 -0
- package/template/apps/asas-backoffice/src/start.ts +42 -0
- package/template/apps/asas-backoffice/src/styles.css +202 -0
- package/template/apps/asas-backoffice/src/theme.ts +2 -0
- package/template/apps/asas-backoffice/tsconfig.json +19 -0
- package/template/apps/asas-backoffice/vite.config.ts +32 -0
- package/template/apps/asas-tanstack/components.json +25 -0
- package/template/apps/asas-tanstack/gitignore +3 -0
- package/template/apps/asas-tanstack/package.json +58 -0
- package/template/apps/asas-tanstack/public/favicon.svg +1 -0
- package/template/apps/asas-tanstack/src/api.ts +147 -0
- package/template/apps/asas-tanstack/src/auth-frame.tsx +150 -0
- package/template/apps/asas-tanstack/src/choose-password.tsx +150 -0
- package/template/apps/asas-tanstack/src/components/ui/accordion.tsx +10 -0
- package/template/apps/asas-tanstack/src/components/ui/alert-dialog.tsx +173 -0
- package/template/apps/asas-tanstack/src/components/ui/avatar.tsx +104 -0
- package/template/apps/asas-tanstack/src/components/ui/badge.tsx +48 -0
- package/template/apps/asas-tanstack/src/components/ui/breadcrumb.tsx +125 -0
- package/template/apps/asas-tanstack/src/components/ui/button-group.tsx +87 -0
- package/template/apps/asas-tanstack/src/components/ui/button.tsx +56 -0
- package/template/apps/asas-tanstack/src/components/ui/calendar.tsx +223 -0
- package/template/apps/asas-tanstack/src/components/ui/card.tsx +94 -0
- package/template/apps/asas-tanstack/src/components/ui/chart.tsx +369 -0
- package/template/apps/asas-tanstack/src/components/ui/checkbox.tsx +27 -0
- package/template/apps/asas-tanstack/src/components/ui/collapsible.tsx +19 -0
- package/template/apps/asas-tanstack/src/components/ui/combobox.tsx +300 -0
- package/template/apps/asas-tanstack/src/components/ui/dialog.tsx +149 -0
- package/template/apps/asas-tanstack/src/components/ui/direction.tsx +5 -0
- package/template/apps/asas-tanstack/src/components/ui/directional-icon.tsx +20 -0
- package/template/apps/asas-tanstack/src/components/ui/dropdown-menu.tsx +267 -0
- package/template/apps/asas-tanstack/src/components/ui/field.tsx +225 -0
- package/template/apps/asas-tanstack/src/components/ui/input-group.tsx +147 -0
- package/template/apps/asas-tanstack/src/components/ui/input-otp.tsx +20 -0
- package/template/apps/asas-tanstack/src/components/ui/input.tsx +20 -0
- package/template/apps/asas-tanstack/src/components/ui/label.tsx +18 -0
- package/template/apps/asas-tanstack/src/components/ui/popover.tsx +50 -0
- package/template/apps/asas-tanstack/src/components/ui/radio-group.tsx +10 -0
- package/template/apps/asas-tanstack/src/components/ui/resizable.tsx +47 -0
- package/template/apps/asas-tanstack/src/components/ui/scroll-area.tsx +52 -0
- package/template/apps/asas-tanstack/src/components/ui/select.tsx +190 -0
- package/template/apps/asas-tanstack/src/components/ui/separator.tsx +23 -0
- package/template/apps/asas-tanstack/src/components/ui/sheet.tsx +127 -0
- package/template/apps/asas-tanstack/src/components/ui/sidebar.tsx +716 -0
- package/template/apps/asas-tanstack/src/components/ui/skeleton.tsx +13 -0
- package/template/apps/asas-tanstack/src/components/ui/slider.tsx +13 -0
- package/template/apps/asas-tanstack/src/components/ui/spinner.tsx +11 -0
- package/template/apps/asas-tanstack/src/components/ui/switch.tsx +8 -0
- package/template/apps/asas-tanstack/src/components/ui/table.tsx +119 -0
- package/template/apps/asas-tanstack/src/components/ui/tabs.tsx +80 -0
- package/template/apps/asas-tanstack/src/components/ui/textarea.tsx +18 -0
- package/template/apps/asas-tanstack/src/components/ui/toggle-group.tsx +8 -0
- package/template/apps/asas-tanstack/src/components/ui/toggle.tsx +44 -0
- package/template/apps/asas-tanstack/src/components/ui/tooltip.tsx +68 -0
- package/template/apps/asas-tanstack/src/components.ts +35 -0
- package/template/apps/asas-tanstack/src/copy.ts +1207 -0
- package/template/apps/asas-tanstack/src/first-run.ts +16 -0
- package/template/apps/asas-tanstack/src/hooks/use-mobile.ts +19 -0
- package/template/apps/asas-tanstack/src/lib/utils.ts +6 -0
- package/template/apps/asas-tanstack/src/links.ts +38 -0
- package/template/apps/asas-tanstack/src/locale-switch.tsx +28 -0
- package/template/apps/asas-tanstack/src/no-organization.tsx +108 -0
- package/template/apps/asas-tanstack/src/quiet-abort.ts +25 -0
- package/template/apps/asas-tanstack/src/redirect.ts +39 -0
- package/template/apps/asas-tanstack/src/request-headers.ts +11 -0
- package/template/apps/asas-tanstack/src/routeTree.gen.ts +424 -0
- package/template/apps/asas-tanstack/src/router.tsx +18 -0
- package/template/apps/asas-tanstack/src/routes/(screens)/dashboard/files.ts +11 -0
- package/template/apps/asas-tanstack/src/routes/(screens)/dashboard/screen.json +4242 -0
- package/template/apps/asas-tanstack/src/routes/(screens)/dashboard/types.ts +160 -0
- package/template/apps/asas-tanstack/src/routes/(screens)/dashboard.$.tsx +17 -0
- package/template/apps/asas-tanstack/src/routes/(screens)/dashboard.index.tsx +10 -0
- package/template/apps/asas-tanstack/src/routes/(screens)/dashboard.tsx +13 -0
- package/template/apps/asas-tanstack/src/routes/(screens)/portal/files.ts +9 -0
- package/template/apps/asas-tanstack/src/routes/(screens)/portal/screen.json +1564 -0
- package/template/apps/asas-tanstack/src/routes/(screens)/portal/types.ts +94 -0
- package/template/apps/asas-tanstack/src/routes/(screens)/portal.$.tsx +17 -0
- package/template/apps/asas-tanstack/src/routes/(screens)/portal.index.tsx +10 -0
- package/template/apps/asas-tanstack/src/routes/(screens)/portal.tsx +13 -0
- package/template/apps/asas-tanstack/src/routes/__root.tsx +99 -0
- package/template/apps/asas-tanstack/src/routes/api/$.ts +11 -0
- package/template/apps/asas-tanstack/src/routes/auth.accept-invitation.tsx +108 -0
- package/template/apps/asas-tanstack/src/routes/auth.first-run.tsx +114 -0
- package/template/apps/asas-tanstack/src/routes/auth.forgot-password.tsx +94 -0
- package/template/apps/asas-tanstack/src/routes/auth.handoff.ts +39 -0
- package/template/apps/asas-tanstack/src/routes/auth.reset-password.tsx +16 -0
- package/template/apps/asas-tanstack/src/routes/auth.set-password.tsx +16 -0
- package/template/apps/asas-tanstack/src/routes/auth.sign-in.tsx +98 -0
- package/template/apps/asas-tanstack/src/routes/auth.sign-up.tsx +143 -0
- package/template/apps/asas-tanstack/src/routes/index.tsx +146 -0
- package/template/apps/asas-tanstack/src/routes/unauthorized.tsx +134 -0
- package/template/apps/asas-tanstack/src/screen-form.tsx +36 -0
- package/template/apps/asas-tanstack/src/screen-list.tsx +140 -0
- package/template/apps/asas-tanstack/src/screen-names.ts +10 -0
- package/template/apps/asas-tanstack/src/screens.tsx +888 -0
- package/template/apps/asas-tanstack/src/server.ts +26 -0
- package/template/apps/asas-tanstack/src/slide.ts +22 -0
- package/template/apps/asas-tanstack/src/start.ts +57 -0
- package/template/apps/asas-tanstack/src/styles.css +163 -0
- package/template/apps/asas-tanstack/src/theme.ts +5 -0
- package/template/apps/asas-tanstack/tsconfig.json +19 -0
- package/template/apps/asas-tanstack/vite.config.ts +34 -0
- package/template/package.json +16 -0
- package/template/templates/asas/.claude/skills/add-field/SKILL.md +27 -0
- package/template/templates/asas/.claude/skills/add-kayan/SKILL.md +26 -0
- package/template/templates/asas/.claude/skills/eject-admin-piece/SKILL.md +28 -0
- package/template/templates/asas/.claude/skills/upgrade-mithaq/SKILL.md +35 -0
- package/template/templates/asas/.env.example +33 -0
- package/template/templates/asas/AGENTS.md +101 -0
- package/template/templates/asas/CLAUDE.md +1 -0
- package/template/templates/asas/README.md +1056 -0
- package/template/templates/asas/mithaq.hints.json +25 -0
- package/template/templates/asas/package.json +53 -0
- package/template/templates/asas/src/asas.ts +118 -0
- package/template/templates/asas/src/backoffice-http.ts +117 -0
- package/template/templates/asas/src/dev-setup.ts +112 -0
- package/template/templates/asas/src/local-database.ts +22 -0
- package/template/templates/asas/src/messages/account.json +132 -0
- package/template/templates/asas/src/messages/auditLog.json +304 -0
- package/template/templates/asas/src/messages/backofficeLog.json +132 -0
- package/template/templates/asas/src/messages/courses.json +312 -0
- package/template/templates/asas/src/messages/enrollments.json +60 -0
- package/template/templates/asas/src/messages/invitation.json +82 -0
- package/template/templates/asas/src/messages/localUsers.json +48 -0
- package/template/templates/asas/src/messages/media.json +76 -0
- package/template/templates/asas/src/messages/member.json +62 -0
- package/template/templates/asas/src/messages/memberRoles.json +48 -0
- package/template/templates/asas/src/messages/mithaq.json +214 -0
- package/template/templates/asas/src/messages/organization.json +72 -0
- package/template/templates/asas/src/messages/organizationRole.json +62 -0
- package/template/templates/asas/src/messages/roles.json +72 -0
- package/template/templates/asas/src/messages/session.json +102 -0
- package/template/templates/asas/src/messages/sessions.json +136 -0
- package/template/templates/asas/src/messages/settings.json +64 -0
- package/template/templates/asas/src/messages/tags.json +48 -0
- package/template/templates/asas/src/messages/user.json +112 -0
- package/template/templates/asas/src/messages/verification.json +62 -0
- package/template/templates/asas/src/migrations/20260920080701_parched_tombstone/migration.sql +177 -0
- package/template/templates/asas/src/migrations/20260920080701_parched_tombstone/snapshot.json +2169 -0
- package/template/templates/asas/src/migrations/20260920230505_zippy_ben_urich/migration.sql +1 -0
- package/template/templates/asas/src/migrations/20260920230505_zippy_ben_urich/snapshot.json +2182 -0
- package/template/templates/asas/src/migrations/20260920231219_slim_retro_girl/migration.sql +20 -0
- package/template/templates/asas/src/migrations/20260920231219_slim_retro_girl/snapshot.json +2409 -0
- package/template/templates/asas/src/migrations/20260922082945_military_ironclad/migration.sql +15 -0
- package/template/templates/asas/src/migrations/20260922082945_military_ironclad/snapshot.json +2627 -0
- package/template/templates/asas/src/migrations/20260922112630_bitter_chat/migration.sql +5 -0
- package/template/templates/asas/src/migrations/20260922112630_bitter_chat/snapshot.json +2691 -0
- package/template/templates/asas/src/migrations/20260923235915_foamy_rachel_grey/migration.sql +8 -0
- package/template/templates/asas/src/migrations/20260923235915_foamy_rachel_grey/snapshot.json +2795 -0
- package/template/templates/asas/src/migrations/20260926145709_clean_the_initiative/migration.sql +3 -0
- package/template/templates/asas/src/migrations/20260926145709_clean_the_initiative/snapshot.json +2834 -0
- package/template/templates/asas/src/migrations/20260927212525_bent_prowler/migration.sql +9 -0
- package/template/templates/asas/src/migrations/20260927212525_bent_prowler/snapshot.json +2870 -0
- package/template/templates/asas/src/migrations/20260927223441_tranquil_bromley/migration.sql +1 -0
- package/template/templates/asas/src/migrations/20260927223441_tranquil_bromley/snapshot.json +2881 -0
- package/template/templates/asas/src/migrations/20260928030047_outgoing_wraith/migration.sql +15 -0
- package/template/templates/asas/src/migrations/20260928030047_outgoing_wraith/snapshot.json +3073 -0
- package/template/templates/asas/src/migrations/20260929024407_grey_morlun/migration.sql +21 -0
- package/template/templates/asas/src/migrations/20260929024407_grey_morlun/snapshot.json +3098 -0
- package/template/templates/asas/src/migrations/20260929130530_bored_jackal/migration.sql +21 -0
- package/template/templates/asas/src/migrations/20260929130530_bored_jackal/snapshot.json +3124 -0
- package/template/templates/asas/src/migrations/20260930102924_material_newton_destine/migration.sql +32 -0
- package/template/templates/asas/src/migrations/20260930102924_material_newton_destine/snapshot.json +3324 -0
- package/template/templates/asas/src/migrations/20261003022353_clumsy_taskmaster/migration.sql +2 -0
- package/template/templates/asas/src/migrations/20261003022353_clumsy_taskmaster/snapshot.json +3350 -0
- package/template/templates/asas/src/migrations/20261003143249_premium_red_shift/migration.sql +21 -0
- package/template/templates/asas/src/migrations/20261003143249_premium_red_shift/snapshot.json +3635 -0
- package/template/templates/asas/src/migrations/20261008215349_square_norrin_radd/migration.sql +8 -0
- package/template/templates/asas/src/migrations/20261008215349_square_norrin_radd/snapshot.json +3635 -0
- package/template/templates/asas/src/migrations/20261008230204_many_scarecrow/migration.sql +18 -0
- package/template/templates/asas/src/migrations/20261008230204_many_scarecrow/snapshot.json +3635 -0
- package/template/templates/asas/src/migrations/20261010005521_elite_giant_girl/migration.sql +18 -0
- package/template/templates/asas/src/migrations/20261010005521_elite_giant_girl/snapshot.json +3635 -0
- package/template/templates/asas/src/migrations/20261010120822_needy_thunderbolt/migration.sql +11 -0
- package/template/templates/asas/src/migrations/20261010120822_needy_thunderbolt/snapshot.json +3667 -0
- package/template/templates/asas/src/migrations/20261012000752_organization_ownership/migration.sql +48 -0
- package/template/templates/asas/src/migrations/20261012000752_organization_ownership/snapshot.json +3891 -0
- package/template/templates/asas/src/migrations/20261014221355_tags_restrict/migration.sql +1 -0
- package/template/templates/asas/src/migrations/20261014221355_tags_restrict/snapshot.json +3881 -0
- package/template/templates/asas/src/migrations/20261014222218_one_seat_per_session/migration.sql +1 -0
- package/template/templates/asas/src/migrations/20261014222218_one_seat_per_session/snapshot.json +3894 -0
- package/template/templates/asas/src/migrations/20261014223522_session_archive_reason/migration.sql +1 -0
- package/template/templates/asas/src/migrations/20261014223522_session_archive_reason/snapshot.json +3907 -0
- package/template/templates/asas/src/migrations/20261014225728_course_archive_by_operation/migration.sql +1 -0
- package/template/templates/asas/src/migrations/20261014225728_course_archive_by_operation/snapshot.json +3907 -0
- package/template/templates/asas/src/migrations/20261015042444_organization_names/migration.sql +1 -0
- package/template/templates/asas/src/migrations/20261015042444_organization_names/snapshot.json +3920 -0
- package/template/templates/asas/src/migrations/20261015053212_member_last_seen/migration.sql +1 -0
- package/template/templates/asas/src/migrations/20261015053212_member_last_seen/snapshot.json +3933 -0
- package/template/templates/asas/src/migrations/20261015093203_activity_and_staff_history/migration.sql +15 -0
- package/template/templates/asas/src/migrations/20261015093203_activity_and_staff_history/snapshot.json +4113 -0
- package/template/templates/asas/src/migrations/20261015095752_portal/migration.sql +2 -0
- package/template/templates/asas/src/migrations/20261015095752_portal/snapshot.json +4113 -0
- package/template/templates/asas/src/migrations/20261015135529_better_auth_indexes/migration.sql +9 -0
- package/template/templates/asas/src/migrations/20261015135529_better_auth_indexes/snapshot.json +4302 -0
- package/template/templates/asas/src/migrations/20261015162438_audit_changes_json/migration.sql +27 -0
- package/template/templates/asas/src/migrations/20261015162438_audit_changes_json/snapshot.json +4289 -0
- package/template/templates/asas/src/migrations/drizzle.config.ts +11 -0
- package/template/templates/asas/src/migrations/schema.ts +30 -0
- package/template/templates/asas/src/mithaq.ts +4 -0
- package/template/templates/asas/src/organizations.ts +3 -0
- package/template/templates/asas/src/seed.ts +427 -0
- package/template/templates/asas/src/ui-backoffice/home.tsx +63 -0
- package/template/templates/asas/src/ui-backoffice/organization-actions.tsx +121 -0
- package/template/templates/asas/src/ui-backoffice/record-page.tsx +30 -0
- package/template/templates/asas/src/ui-backoffice/role-cells.tsx +59 -0
- package/template/templates/asas/src/ui-backoffice/user-actions.tsx +249 -0
- package/template/templates/asas/src/ui-dashboard/dashboard.tsx +300 -0
- package/template/templates/asas/src/ui-dashboard/home.tsx +73 -0
- package/template/templates/asas/src/ui-dashboard/members-card.tsx +147 -0
- package/template/templates/asas/src/ui-dashboard/members.tsx +829 -0
- package/template/templates/asas/src/ui-dashboard/theme-provider.tsx +10 -0
- package/template/templates/asas/src/ui-dashboard/versions.tsx +446 -0
- package/template/templates/asas/src/ui-portal/courses-home.tsx +96 -0
- package/template/templates/asas/src/ui-portal/home.tsx +73 -0
- package/template/templates/asas/src/ui-portal/page.tsx +11 -0
- package/template/templates/features/audit-log/audit-log/audit-log.ts +284 -0
- package/template/templates/features/audit-log/backoffice-log/backoffice-log.ts +67 -0
- package/template/templates/features/audit-log/index.ts +4 -0
- package/template/templates/features/audit-log/package.json +13 -0
- package/template/templates/features/auth/index.ts +11 -0
- package/template/templates/features/auth/local-users/local-users.ts +143 -0
- package/template/templates/features/auth/package.json +13 -0
- package/template/templates/features/auth/user/account.access.ts +8 -0
- package/template/templates/features/auth/user/account.ts +31 -0
- package/template/templates/features/auth/user/index.ts +18 -0
- package/template/templates/features/auth/user/invitation.access.ts +4 -0
- package/template/templates/features/auth/user/invitation.ts +27 -0
- package/template/templates/features/auth/user/member.access.ts +10 -0
- package/template/templates/features/auth/user/member.screens.ts +37 -0
- package/template/templates/features/auth/user/member.ts +26 -0
- package/template/templates/features/auth/user/organization.access.ts +20 -0
- package/template/templates/features/auth/user/organization.screens.ts +37 -0
- package/template/templates/features/auth/user/organization.ts +25 -0
- package/template/templates/features/auth/user/organizationRole.access.ts +8 -0
- package/template/templates/features/auth/user/organizationRole.ts +25 -0
- package/template/templates/features/auth/user/session.access.ts +8 -0
- package/template/templates/features/auth/user/session.ts +28 -0
- package/template/templates/features/auth/user/user.access.ts +30 -0
- package/template/templates/features/auth/user/user.screens.ts +45 -0
- package/template/templates/features/auth/user/user.ts +31 -0
- package/template/templates/features/auth/user/verification.access.ts +8 -0
- package/template/templates/features/auth/user/verification.ts +23 -0
- package/template/templates/features/courses/courses/courses.ts +601 -0
- package/template/templates/features/courses/courses/cover-reads.ts +33 -0
- package/template/templates/features/courses/courses/ui/archived-badge.tsx +9 -0
- package/template/templates/features/courses/courses/ui/fee-cell.tsx +19 -0
- package/template/templates/features/courses/courses/ui/no-courses.tsx +13 -0
- package/template/templates/features/courses/courses/ui/status-cell.tsx +33 -0
- package/template/templates/features/courses/courses/ui/unpublish-button.tsx +15 -0
- package/template/templates/features/courses/domain/archive.ts +39 -0
- package/template/templates/features/courses/domain/on-offer.ts +6 -0
- package/template/templates/features/courses/enrollments/enrollments.ts +309 -0
- package/template/templates/features/courses/enrollments/ui/session-cell.tsx +75 -0
- package/template/templates/features/courses/enrollments/ui/session-labels.tsx +36 -0
- package/template/templates/features/courses/index.ts +8 -0
- package/template/templates/features/courses/package.json +24 -0
- package/template/templates/features/courses/portal/ui/catalogue.tsx +86 -0
- package/template/templates/features/courses/portal/ui/course.tsx +176 -0
- package/template/templates/features/courses/portal/ui/enrollments.tsx +170 -0
- package/template/templates/features/courses/portal/ui/parts.tsx +95 -0
- package/template/templates/features/courses/portal/ui/reads.ts +76 -0
- package/template/templates/features/courses/portal/ui/rules.ts +87 -0
- package/template/templates/features/courses/portal/ui/shell.tsx +194 -0
- package/template/templates/features/courses/sessions/seats.ts +42 -0
- package/template/templates/features/courses/sessions/sessions.ts +186 -0
- package/template/templates/features/media/index.ts +10 -0
- package/template/templates/features/media/media/media.ts +131 -0
- package/template/templates/features/media/media/ui/preview.tsx +143 -0
- package/template/templates/features/media/package.json +19 -0
- package/template/templates/features/roles/index.ts +4 -0
- package/template/templates/features/roles/member-roles/member-roles.ts +207 -0
- package/template/templates/features/roles/package.json +21 -0
- package/template/templates/features/roles/roles/roles.ts +184 -0
- package/template/templates/features/roles/roles/ui/role-form.tsx +267 -0
- package/template/templates/features/roles/roles/ui/roles.tsx +190 -0
- package/template/templates/features/settings/index.ts +3 -0
- package/template/templates/features/settings/package.json +13 -0
- package/template/templates/features/settings/settings/settings.ts +64 -0
- package/template/templates/features/tags/index.ts +3 -0
- package/template/templates/features/tags/package.json +14 -0
- package/template/templates/features/tags/tags/tags.ts +80 -0
- package/template/templates/mithaq/components.json +22 -0
- package/template/templates/mithaq/package.json +33 -0
- package/template/templates/mithaq/src/mail/index.ts +32 -0
- package/template/templates/mithaq/src/mail/mailers.ts +36 -0
- package/template/templates/mithaq/src/mithaq/audit-sink.ts +111 -0
- package/template/templates/mithaq/src/mithaq/auth/backoffice-roles.ts +108 -0
- package/template/templates/mithaq/src/mithaq/auth/index.ts +1859 -0
- package/template/templates/mithaq/src/mithaq/auth/organizations.ts +80 -0
- package/template/templates/mithaq/src/mithaq/pages.ts +83 -0
- package/template/templates/mithaq/src/mithaq/rich-text-nodes.ts +19 -0
- package/template/templates/mithaq/src/mithaq.ts +593 -0
- package/template/templates/mithaq/src/roles/default-member-roles.ts +29 -0
- package/template/templates/mithaq/src/roles/grants.ts +47 -0
- package/template/templates/mithaq/src/roles/member-access.ts +11 -0
- package/template/templates/mithaq/src/roles/statements.ts +13 -0
- package/template/templates/mithaq/src/roles/system-roles.ts +16 -0
- package/template/templates/mithaq/src/roles/tenant-access.ts +260 -0
- package/template/templates/mithaq/src/roles/tenant-roles.ts +321 -0
- package/template/templates/mithaq/src/steps/expected-updated-at.ts +41 -0
- package/template/templates/mithaq/src/storage/defaults.ts +156 -0
- package/template/templates/mithaq/src/storage/index.ts +97 -0
- package/template/templates/mithaq/src/storage/local.ts +143 -0
- package/template/templates/mithaq/src/storage/r2.ts +5 -0
- package/template/templates/mithaq/src/storage/s3.ts +91 -0
- package/template/templates/mithaq/src/storage/uploadthing.ts +59 -0
- package/template/templates/mithaq/src/ui/activity-page.tsx +33 -0
- package/template/templates/mithaq/src/ui/activity.tsx +527 -0
- package/template/templates/mithaq/src/ui/app-shell.tsx +268 -0
- package/template/templates/mithaq/src/ui/kpi-card.tsx +25 -0
- package/template/templates/mithaq/src/ui/record-page.tsx +50 -0
- package/template/templates/mithaq/src/ui/rich-text-nodes.tsx +33 -0
- package/template/templates/mithaq/src/ui/screen-nav.tsx +28 -0
- package/template/templates/mithaq/src/ui/table-page.tsx +9 -0
- package/template/templates/mithaq/src/ui/top-shell.tsx +60 -0
- package/README.md +0 -3
|
@@ -0,0 +1,1207 @@
|
|
|
1
|
+
// Imports no router, stylesheet or React, so a test can load this under plain `node`.
|
|
2
|
+
import { flattenMessageFile } from 'mithaq/admin'
|
|
3
|
+
import { organizations, showOrganization } from 'asas/organizations'
|
|
4
|
+
import localUsers from 'asas/messages/localUsers.json' with { type: 'json' }
|
|
5
|
+
import organization from 'asas/messages/organization.json' with { type: 'json' }
|
|
6
|
+
import tags from 'asas/messages/tags.json' with { type: 'json' }
|
|
7
|
+
// add-on: audit-log
|
|
8
|
+
import auditLog from 'asas/messages/auditLog.json' with { type: 'json' }
|
|
9
|
+
import backofficeLog from 'asas/messages/backofficeLog.json' with { type: 'json' }
|
|
10
|
+
// end add-on: audit-log
|
|
11
|
+
// add-on: courses
|
|
12
|
+
import courses from 'asas/messages/courses.json' with { type: 'json' }
|
|
13
|
+
import enrollments from 'asas/messages/enrollments.json' with { type: 'json' }
|
|
14
|
+
import sessions from 'asas/messages/sessions.json' with { type: 'json' }
|
|
15
|
+
// end add-on: courses
|
|
16
|
+
// add-on: media
|
|
17
|
+
import media from 'asas/messages/media.json' with { type: 'json' }
|
|
18
|
+
// end add-on: media
|
|
19
|
+
// add-on: roles
|
|
20
|
+
import memberRoles from 'asas/messages/memberRoles.json' with { type: 'json' }
|
|
21
|
+
import roles from 'asas/messages/roles.json' with { type: 'json' }
|
|
22
|
+
// end add-on: roles
|
|
23
|
+
// add-on: settings
|
|
24
|
+
import settings from 'asas/messages/settings.json' with { type: 'json' }
|
|
25
|
+
// end add-on: settings
|
|
26
|
+
|
|
27
|
+
// Restated, not imported: the original lives in a `.tsx` file and the test runner has no JSX compiler.
|
|
28
|
+
type Translate = (key: string, fallback?: string) => string | undefined
|
|
29
|
+
|
|
30
|
+
/** The locales this project declares. */
|
|
31
|
+
export const locales = ['ar', 'en'] as const
|
|
32
|
+
|
|
33
|
+
/** The app reads either message-file format during the upgrade release. */
|
|
34
|
+
export const copyFile = (file: Record<string, unknown>): Record<string, Record<string, string>> =>
|
|
35
|
+
Object.fromEntries(Object.entries(flattenMessageFile({ file, locales })).map(([key, value]) =>
|
|
36
|
+
[key, typeof value === 'string' ? Object.fromEntries(locales.map((locale) => [locale, value])) : value ?? {}]))
|
|
37
|
+
|
|
38
|
+
/** One of the locales this project declares. */
|
|
39
|
+
export type Locale = (typeof locales)[number]
|
|
40
|
+
|
|
41
|
+
/** The name of the cookie that carries a viewer's chosen locale. */
|
|
42
|
+
export const localeCookie = 'locale'
|
|
43
|
+
|
|
44
|
+
const localeCookieMaxAge = 60 * 60 * 24 * 365
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The cookie value for a chosen locale. No `HttpOnly`, because the browser script
|
|
48
|
+
* writes it; no `Secure`, because the dev server is plain HTTP and the choice is not a credential.
|
|
49
|
+
*/
|
|
50
|
+
export const localeCookieValue = (locale: Locale): string =>
|
|
51
|
+
`${localeCookie}=${locale}; Path=/; Max-Age=${localeCookieMaxAge}; SameSite=Lax`
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Which locale one request reads in. The value comes from a request header a caller
|
|
55
|
+
* writes, so anything but a declared locale becomes `en`; nothing downstream asks again.
|
|
56
|
+
*/
|
|
57
|
+
export const localeOf = (asked: string | null | undefined): Locale =>
|
|
58
|
+
(locales as readonly string[]).includes(asked ?? '') ? (asked as Locale) : 'en'
|
|
59
|
+
|
|
60
|
+
/** Which way a document in one locale runs. */
|
|
61
|
+
export const dirOf = (locale: Locale): 'rtl' | 'ltr' => (locale === 'ar' ? 'rtl' : 'ltr')
|
|
62
|
+
|
|
63
|
+
/** The copy every organization mode starts from; `scopes` below lays the mode's words over it. */
|
|
64
|
+
const ownScopes: Record<string, Record<string, Record<string, string>>> = {
|
|
65
|
+
// add-on: courses
|
|
66
|
+
courses: {
|
|
67
|
+
...copyFile(courses),
|
|
68
|
+
'delete.confirm': { en: 'Delete this course', ar: 'احذف هذه الدورة' },
|
|
69
|
+
dangerZoneWarning: { en: 'These actions change this course for everyone.', ar: 'هذه الإجراءات تغير هذه الدورة للجميع.' },
|
|
70
|
+
none: { en: 'No courses yet.', ar: 'لا توجد دورات بعد.' },
|
|
71
|
+
noneOnOffer: { en: 'No courses on offer yet.', ar: 'لا توجد دورات معروضة بعد.' },
|
|
72
|
+
'operations.archive': { en: 'Archive', ar: 'أرشفة' },
|
|
73
|
+
'operations.archive.warning': { en: 'This archives {name}.', ar: 'يؤرشف هذا الإجراء {name}.' },
|
|
74
|
+
'operations.findWithSessions': { en: 'View with its sessions', ar: 'عرض مع جلساتها' },
|
|
75
|
+
new: { en: 'New course', ar: 'دورة جديدة' },
|
|
76
|
+
'archived.true': { en: 'Archived', ar: 'مؤرشف' },
|
|
77
|
+
'archived.false': { en: 'Active', ar: 'نشط' },
|
|
78
|
+
'operations.archive.reason.required': {
|
|
79
|
+
en: 'An archive reason is required.',
|
|
80
|
+
ar: 'سبب الأرشفة مطلوب.',
|
|
81
|
+
},
|
|
82
|
+
'operations.archive.reason.maxLength': {
|
|
83
|
+
en: 'An archive reason is at most 200 characters.',
|
|
84
|
+
ar: 'سبب الأرشفة 200 حرف على الأكثر.',
|
|
85
|
+
},
|
|
86
|
+
'operations.unarchive': { en: 'Unarchive', ar: 'إلغاء الأرشفة' },
|
|
87
|
+
'operations.unarchive.reason.required': { en: 'A reason to bring it back is required.', ar: 'سبب إلغاء الأرشفة مطلوب.' },
|
|
88
|
+
'operations.unarchive.reason.maxLength': { en: 'A reason to bring it back is at most 200 characters.', ar: 'سبب إلغاء الأرشفة 200 حرف على الأكثر.' },
|
|
89
|
+
'operations.unpublish': { en: 'Unpublish', ar: 'إلغاء النشر' },
|
|
90
|
+
},
|
|
91
|
+
// end add-on: courses
|
|
92
|
+
admin: {
|
|
93
|
+
nav: { en: 'Admin', ar: 'الإدارة' },
|
|
94
|
+
yes: { en: 'Yes', ar: 'نعم' },
|
|
95
|
+
no: { en: 'No', ar: 'لا' },
|
|
96
|
+
navigation: { en: 'Navigation', ar: 'التنقل' },
|
|
97
|
+
navigationAbout: { en: 'The pages you can open.', ar: 'الصفحات التي يمكنك فتحها.' },
|
|
98
|
+
mainNavigation: { en: 'Main navigation', ar: 'التنقل الرئيسي' },
|
|
99
|
+
notFound: { en: 'That page does not exist.', ar: 'هذه الصفحة غير موجودة.' },
|
|
100
|
+
notFoundTitle: { en: 'Page not found', ar: 'الصفحة غير موجودة' },
|
|
101
|
+
goHome: { en: 'Go to the home page', ar: 'الذهاب إلى الصفحة الرئيسية' },
|
|
102
|
+
new: { en: 'New', ar: 'جديد' },
|
|
103
|
+
createTitle: { en: 'Create {kind}', ar: 'إنشاء {kind}' },
|
|
104
|
+
save: { en: 'Save', ar: 'حفظ' },
|
|
105
|
+
afterCommitFailed: {
|
|
106
|
+
en: 'Saved, but a follow-up action failed, so something this save should have caused, such as an email or a notification, may be missing. Please contact an administrator.',
|
|
107
|
+
ar: 'تم الحفظ، لكن تعذر إجراء لاحق، فقد يكون ما كان ينبغي أن يتبع هذا الحفظ، كرسالة بريد أو إشعار، مفقودا. يرجى التواصل مع المسؤول.',
|
|
108
|
+
},
|
|
109
|
+
delete: { en: 'Delete', ar: 'حذف' },
|
|
110
|
+
impersonating: { en: 'You are impersonating', ar: 'أنت تنتحل صفة' },
|
|
111
|
+
stopImpersonating: { en: 'Stop impersonating', ar: 'إيقاف انتحال الصفة' },
|
|
112
|
+
cancel: { en: 'Cancel', ar: 'إلغاء' },
|
|
113
|
+
close: { en: 'Close', ar: 'إغلاق' },
|
|
114
|
+
'done.created': { en: '{kind} created', ar: 'تم إنشاء {kind}' },
|
|
115
|
+
'done.saved': { en: '{kind} saved', ar: 'تم حفظ {kind}' },
|
|
116
|
+
'done.published': { en: '{kind} published', ar: 'تم نشر {kind}' },
|
|
117
|
+
'done.draftSaved': { en: 'Draft saved', ar: 'تم حفظ المسودة' },
|
|
118
|
+
'done.deleted': { en: '{kind} deleted', ar: 'تم حذف {kind}' },
|
|
119
|
+
'done.archive': { en: '{kind} archived', ar: 'تمت أرشفة {kind}' },
|
|
120
|
+
'done.unarchive': { en: '{kind} unarchived', ar: 'تم إلغاء أرشفة {kind}' },
|
|
121
|
+
'done.unpublish': { en: '{kind} unpublished', ar: 'تم إلغاء نشر {kind}' },
|
|
122
|
+
'done.restoreVersion': { en: 'Version restored', ar: 'استُعيدت النسخة' },
|
|
123
|
+
'done.ran': { en: '{operation}: done', ar: '{operation}: تم' },
|
|
124
|
+
requestFailed: { en: 'Something went wrong, so nothing was saved. Try again.', ar: 'حدث خطأ، فلم يُحفظ شيء. حاول مرة أخرى.' },
|
|
125
|
+
deleteWarning: { en: 'This cannot be undone.', ar: 'لا يمكن التراجع عن هذا.' },
|
|
126
|
+
deleteConfirm: { en: 'Delete for good', ar: 'احذف نهائيا' },
|
|
127
|
+
deleteFailed: { en: 'Something went wrong, so nothing was deleted. Try again.', ar: 'حدث خطأ، فلم يُحذف شيء. حاول مرة أخرى.' },
|
|
128
|
+
discard: { en: 'Discard changes', ar: 'تجاهل التغييرات' },
|
|
129
|
+
discardWarning: {
|
|
130
|
+
en: 'You have unsaved changes. Leave without saving?',
|
|
131
|
+
ar: 'توجد تغييرات غير محفوظة. هل تريد المغادرة دون حفظ؟',
|
|
132
|
+
},
|
|
133
|
+
discardKeepsDraft: { en: 'The draft saved so far is kept.', ar: 'تبقى المسودة المحفوظة حتى الآن.' },
|
|
134
|
+
dangerZone: { en: 'Danger zone', ar: 'منطقة الخطر' },
|
|
135
|
+
dangerZoneWarning: {
|
|
136
|
+
en: 'These actions change this {kind} for everyone.',
|
|
137
|
+
ar: 'هذه الإجراءات تغير هذا السجل ({kind}) للجميع.',
|
|
138
|
+
},
|
|
139
|
+
edit: { en: 'Edit', ar: 'تعديل' },
|
|
140
|
+
view: { en: 'View', ar: 'عرض' },
|
|
141
|
+
rowActions: { en: 'Row actions', ar: 'إجراءات الصف' },
|
|
142
|
+
rowActionsFor: { en: 'Actions for {name}', ar: 'إجراءات {name}' },
|
|
143
|
+
openRow: { en: 'Press Enter to open this row.', ar: 'اضغط Enter لفتح هذا الصف.' },
|
|
144
|
+
untitled: { en: 'Untitled', ar: 'بدون عنوان' },
|
|
145
|
+
openUntitled: { en: 'Untitled, open', ar: 'بدون عنوان، فتح' },
|
|
146
|
+
missingComponent: {
|
|
147
|
+
en: 'No component is registered for "{component}".',
|
|
148
|
+
ar: 'لا يوجد مكون مسجل لـ "{component}".',
|
|
149
|
+
},
|
|
150
|
+
missingLayout: {
|
|
151
|
+
en: 'No component is registered for layout "{layout}".',
|
|
152
|
+
ar: 'لا يوجد مكون مسجل للتخطيط "{layout}".',
|
|
153
|
+
},
|
|
154
|
+
page: { en: 'Page', ar: 'صفحة' },
|
|
155
|
+
perPage: { en: 'Per page', ar: 'لكل صفحة' },
|
|
156
|
+
of: { en: 'of', ar: 'من' },
|
|
157
|
+
first: { en: 'First', ar: 'الأول' },
|
|
158
|
+
previous: { en: 'Previous', ar: 'السابق' },
|
|
159
|
+
next: { en: 'Next', ar: 'التالي' },
|
|
160
|
+
last: { en: 'Last', ar: 'الأخير' },
|
|
161
|
+
search: { en: 'Search', ar: 'بحث' },
|
|
162
|
+
bulkBar: { en: 'Ticked rows', ar: 'الصفوف المحددة' },
|
|
163
|
+
searchThrough: { en: '{relation} {field}', ar: '{field} ({relation})' },
|
|
164
|
+
clearSearch: { en: 'Clear search', ar: 'مسح البحث' },
|
|
165
|
+
filters: { en: 'Filters', ar: 'المرشحات' },
|
|
166
|
+
empty: { en: 'Nothing here yet.', ar: 'لا شيء هنا بعد.' },
|
|
167
|
+
loading: { en: 'Loading…', ar: 'جارٍ التحميل…' },
|
|
168
|
+
clear: { en: 'Clear', ar: 'مسح' },
|
|
169
|
+
unset: { en: 'unset', ar: 'غير محدد' },
|
|
170
|
+
pickDate: { en: 'Pick a date', ar: 'اختر تاريخا' },
|
|
171
|
+
'richText.undo': { en: 'Undo', ar: 'تراجع' },
|
|
172
|
+
'richText.redo': { en: 'Redo', ar: 'إعادة' },
|
|
173
|
+
'richText.toolbar': { en: 'Formatting', ar: 'التنسيق' },
|
|
174
|
+
'richText.blockFormatCurrent': { en: 'Block format: {block}', ar: 'نوع الفقرة: {block}' },
|
|
175
|
+
'richText.bold': { en: 'Bold', ar: 'عريض' },
|
|
176
|
+
'richText.italic': { en: 'Italic', ar: 'مائل' },
|
|
177
|
+
'richText.underline': { en: 'Underline', ar: 'تسطير' },
|
|
178
|
+
'richText.strikethrough': { en: 'Strikethrough', ar: 'يتوسطه خط' },
|
|
179
|
+
'richText.code': { en: 'Code', ar: 'شيفرة' },
|
|
180
|
+
'richText.addLink': { en: 'Add link', ar: 'إضافة رابط' },
|
|
181
|
+
'richText.removeLink': { en: 'Remove link', ar: 'إزالة الرابط' },
|
|
182
|
+
'richText.linkAddress': { en: 'Link address', ar: 'عنوان الرابط' },
|
|
183
|
+
'richText.editLink': { en: 'Edit link', ar: 'تعديل الرابط' },
|
|
184
|
+
'richText.saveLink': { en: 'Save link', ar: 'حفظ الرابط' },
|
|
185
|
+
'richText.cancelLink': { en: 'Cancel', ar: 'إلغاء' },
|
|
186
|
+
'richText.clearFormatting': { en: 'Clear formatting', ar: 'مسح التنسيق' },
|
|
187
|
+
'richText.alignLeft': { en: 'Align left', ar: 'محاذاة لليسار' },
|
|
188
|
+
'richText.alignCenter': { en: 'Align center', ar: 'توسيط' },
|
|
189
|
+
'richText.alignRight': { en: 'Align right', ar: 'محاذاة لليمين' },
|
|
190
|
+
'richText.justify': { en: 'Justify', ar: 'ضبط' },
|
|
191
|
+
'richText.outdent': { en: 'Outdent', ar: 'إنقاص المسافة البادئة' },
|
|
192
|
+
'richText.indent': { en: 'Indent', ar: 'زيادة المسافة البادئة' },
|
|
193
|
+
'richText.unknownNode': {
|
|
194
|
+
en: 'This text holds a "{type}" node this editor cannot draw, so it is shown as its words and cannot be changed here.',
|
|
195
|
+
ar: 'يحتوي هذا النص على عقدة "{type}" لا يستطيع هذا المحرر رسمها، فيُعرض بكلماته ولا يمكن تغييره هنا.',
|
|
196
|
+
},
|
|
197
|
+
'richText.unreadable': {
|
|
198
|
+
en: 'This editor cannot read this text, so it is shown as its words and cannot be changed here.',
|
|
199
|
+
ar: 'لا يستطيع هذا المحرر قراءة هذا النص، فيُعرض بكلماته ولا يمكن تغييره هنا.',
|
|
200
|
+
},
|
|
201
|
+
'upload.choose': { en: 'Choose a file', ar: 'اختر ملفا' },
|
|
202
|
+
'upload.add': { en: 'Add a file', ar: 'أضف ملفا' },
|
|
203
|
+
'upload.new': { en: 'Upload new', ar: 'رفع ملف جديد' },
|
|
204
|
+
'upload.file': { en: 'File', ar: 'الملف' },
|
|
205
|
+
'upload.noFile': { en: 'No file chosen', ar: 'لم يُختر أي ملف' },
|
|
206
|
+
'upload.required': { en: 'Choose a file first.', ar: 'اختر ملفا أولا.' },
|
|
207
|
+
'upload.takes': { en: 'Files of these types, {size} at most: {types}.', ar: 'ملفات من هذه الأنواع، بحد أقصى {size}: {types}.' },
|
|
208
|
+
'upload.send': { en: 'Upload', ar: 'رفع' },
|
|
209
|
+
'upload.sending': { en: 'Uploading… {percent}%', ar: 'جارٍ الرفع… {percent}٪' },
|
|
210
|
+
'upload.retry': { en: 'Try again', ar: 'أعد المحاولة' },
|
|
211
|
+
'upload.missing': { en: 'This file cannot be shown.', ar: 'لا يمكن عرض هذا الملف.' },
|
|
212
|
+
'upload.moveEarlier': { en: 'Move {name} earlier', ar: 'انقل {name} إلى السابق' },
|
|
213
|
+
'upload.moveLater': { en: 'Move {name} later', ar: 'انقل {name} إلى التالي' },
|
|
214
|
+
'upload.remove': { en: 'Remove {name}', ar: 'أزل {name}' },
|
|
215
|
+
'upload.image': { en: 'Image', ar: 'صورة' },
|
|
216
|
+
'upload.insert': { en: 'Insert an image', ar: 'إدراج صورة' },
|
|
217
|
+
'upload.insertHint': { en: 'Choose a file already here, or upload a new one.', ar: 'اختر ملفا موجودا هنا، أو ارفع ملفا جديدا.' },
|
|
218
|
+
time: { en: 'Time', ar: 'الوقت' },
|
|
219
|
+
showOptions: { en: 'Show options', ar: 'إظهار الخيارات' },
|
|
220
|
+
// base-ui names none of the combobox's icon-only buttons (`clear`, `showOptions`, `remove`), so a screen reader needs these.
|
|
221
|
+
remove: { en: 'Remove', ar: 'إزالة' },
|
|
222
|
+
andMore: { en: 'and {count} more', ar: 'و{count} أخرى' },
|
|
223
|
+
noMatches: { en: 'No rows match this filter.', ar: 'لا توجد صفوف تطابق هذا المرشح.' },
|
|
224
|
+
refused: { en: 'That list could not be read.', ar: 'تعذرت قراءة هذه القائمة.' },
|
|
225
|
+
cannotSee: { en: 'You can\'t see this', ar: 'لا يمكنك رؤية هذا' },
|
|
226
|
+
// A held id with no row: one a read of its kayan answered without, and one the viewer may not read.
|
|
227
|
+
deleted: { en: 'Deleted', ar: 'محذوف' },
|
|
228
|
+
notAvailable: { en: 'Not available', ar: 'غير متاح' },
|
|
229
|
+
clearFilters: { en: 'Clear filters', ar: 'مسح المرشحات' },
|
|
230
|
+
selectAll: { en: 'Select every row on this page', ar: 'حدد كل صف في هذه الصفحة' },
|
|
231
|
+
selectRow: { en: 'Select this row', ar: 'حدد هذا الصف' },
|
|
232
|
+
selected: { en: '{count} selected', ar: '{count} محدد' },
|
|
233
|
+
actions: { en: 'Actions', ar: 'الإجراءات' },
|
|
234
|
+
bulkWarning_one: { en: '{plural} you ticked: 1. This applies to it.', ar: 'حددت واحدا من {plural}، وينطبق هذا عليه.' },
|
|
235
|
+
bulkWarning_two: { en: '{plural} you ticked: {count}. This applies to each of them.', ar: 'حددت اثنين من {plural}، وينطبق هذا عليهما.' },
|
|
236
|
+
bulkWarning_few: { en: '{plural} you ticked: {count}. This applies to each of them.', ar: 'حددت {count} من {plural}، وينطبق هذا عليها كلها.' },
|
|
237
|
+
bulkWarning_many: { en: '{plural} you ticked: {count}. This applies to each of them.', ar: 'حددت {count} من {plural}، وينطبق هذا عليها كلها.' },
|
|
238
|
+
bulkWarning_other: { en: '{plural} you ticked: {count}. This applies to each of them.', ar: 'حددت {count} من {plural}، وينطبق هذا عليها كلها.' },
|
|
239
|
+
bulkDone_one: { en: '{plural} done: 1.', ar: 'تم ذلك على واحد من {plural}.' },
|
|
240
|
+
bulkDone_two: { en: '{plural} done: {count}.', ar: 'تم ذلك على اثنين من {plural}.' },
|
|
241
|
+
bulkDone_few: { en: '{plural} done: {count}.', ar: 'تم ذلك على {count} من {plural}.' },
|
|
242
|
+
bulkDone_many: { en: '{plural} done: {count}.', ar: 'تم ذلك على {count} من {plural}.' },
|
|
243
|
+
bulkDone_other: { en: '{plural} done: {count}.', ar: 'تم ذلك على {count} من {plural}.' },
|
|
244
|
+
leaveAsIs: { en: 'Leave as is', ar: 'اتركه كما هو' },
|
|
245
|
+
bulkFilledOnly: { en: 'Only the fields you fill in change.', ar: 'تتغير الحقول التي تملؤها فقط.' },
|
|
246
|
+
bulkNothingFilled: { en: 'Fill in at least one field.', ar: 'املأ حقلًا واحدًا على الأقل.' },
|
|
247
|
+
bulkLeft_one: { en: '1 of the ticked rows was left as it is, because it has never been published.', ar: 'تُرك صف واحد محدد كما هو، لأنه لم يُنشر قط.' },
|
|
248
|
+
bulkLeft_two: { en: '{count} of the ticked rows were left as they are, because they have never been published.', ar: 'تُرك صفان محددان كما هما، لأنهما لم يُنشرا قط.' },
|
|
249
|
+
bulkLeft_few: { en: '{count} of the ticked rows were left as they are, because they have never been published.', ar: 'تُركت {count} صفوف محددة كما هي، لأنها لم تُنشر قط.' },
|
|
250
|
+
bulkLeft_many: { en: '{count} of the ticked rows were left as they are, because they have never been published.', ar: 'تُرك {count} صفا محددا كما هي، لأنها لم تُنشر قط.' },
|
|
251
|
+
bulkLeft_other: { en: '{count} of the ticked rows were left as they are, because they have never been published.', ar: 'تُرك {count} صف محدد كما هي، لأنها لم تُنشر قط.' },
|
|
252
|
+
toolbarWarning: { en: 'This runs on no row of the list.', ar: 'لا يعمل هذا على أي صف من القائمة.' },
|
|
253
|
+
rowWarning: { en: 'This applies to this row.', ar: 'ينطبق هذا على هذا الصف.' },
|
|
254
|
+
rowNamedWarning: { en: 'This applies to {name}.', ar: 'ينطبق هذا على {name}.' },
|
|
255
|
+
noColumns: {
|
|
256
|
+
en: 'Your role can\'t see any column of this list. Ask an administrator of your organization for access.',
|
|
257
|
+
ar: 'لا يستطيع دورك رؤية أي عمود في هذه القائمة. اطلب الوصول من مدير مؤسستك.',
|
|
258
|
+
},
|
|
259
|
+
queryRefused: {
|
|
260
|
+
en: 'This link asks the list for something it does not offer, so nothing was read.',
|
|
261
|
+
ar: 'يطلب هذا الرابط من القائمة ما لا تقدمه، فلم يُقرأ شيء.',
|
|
262
|
+
},
|
|
263
|
+
operationWarning: { en: 'This runs on the row you are editing.', ar: 'يعمل هذا على الصف الذي تحرره.' },
|
|
264
|
+
permissions: { en: 'Permissions', ar: 'الصلاحيات' },
|
|
265
|
+
record: { en: 'Record', ar: 'السجل' },
|
|
266
|
+
invalidFields: { en: 'Fields needing attention: {count}', ar: 'حقول تحتاج إلى مراجعة: {count}' },
|
|
267
|
+
errorSummary: { en: 'Check these fields:', ar: 'راجع هذه الحقول:' },
|
|
268
|
+
addNew: { en: 'Add new {kind}', ar: 'إضافة {kind}' },
|
|
269
|
+
backToList: { en: 'Back to list', ar: 'العودة إلى القائمة' },
|
|
270
|
+
reload: { en: 'Reload', ar: 'إعادة التحميل' },
|
|
271
|
+
hours: { en: 'Hours', ar: 'الساعات' },
|
|
272
|
+
minutes: { en: 'Minutes', ar: 'الدقائق' },
|
|
273
|
+
period: { en: 'AM or PM', ar: 'صباحا أو مساء' },
|
|
274
|
+
organizations: { en: 'Organizations', ar: 'المؤسسات' },
|
|
275
|
+
organization: { en: 'Organization', ar: 'مؤسسة' },
|
|
276
|
+
noOrganization: { en: 'No organization', ar: 'لا توجد مؤسسة' },
|
|
277
|
+
signOut: { en: 'Sign out', ar: 'تسجيل الخروج' },
|
|
278
|
+
theme: { en: 'Toggle theme', ar: 'تبديل المظهر' },
|
|
279
|
+
themeLight: { en: 'Light', ar: 'فاتح' },
|
|
280
|
+
themeDark: { en: 'Dark', ar: 'داكن' },
|
|
281
|
+
themeSystem: { en: 'System', ar: 'النظام' },
|
|
282
|
+
// `toggleSidebar`: the primitive names itself in English alone, so an Arabic viewer hears English unless this is written.
|
|
283
|
+
quickCreate: { en: 'Quick Create', ar: 'إنشاء سريع' },
|
|
284
|
+
inbox: { en: 'Inbox', ar: 'صندوق الوارد' },
|
|
285
|
+
documents: { en: 'Documents', ar: 'المستندات' },
|
|
286
|
+
more: { en: 'More', ar: 'المزيد' },
|
|
287
|
+
open: { en: 'Open', ar: 'فتح' },
|
|
288
|
+
share: { en: 'Share', ar: 'مشاركة' },
|
|
289
|
+
trash: { en: 'Move to Trash', ar: 'نقل إلى المهملات' },
|
|
290
|
+
getHelp: { en: 'Get Help', ar: 'الحصول على مساعدة' },
|
|
291
|
+
toggleSidebar: { en: 'Toggle Sidebar', ar: 'تبديل الشريط الجانبي' },
|
|
292
|
+
},
|
|
293
|
+
portal: {
|
|
294
|
+
home: { en: 'Home', ar: 'الرئيسية' },
|
|
295
|
+
welcome: { en: 'Welcome, {name}', ar: 'مرحبا، {name}' },
|
|
296
|
+
about: { en: 'What you may open here, and how many of each you may read.', ar: 'ما يمكنك فتحه هنا، وعدد ما يمكنك قراءته من كل منها.' },
|
|
297
|
+
open: { en: 'Open the list', ar: 'افتح القائمة' },
|
|
298
|
+
// add-on: courses
|
|
299
|
+
title: { en: 'Portal', ar: 'البوابة' },
|
|
300
|
+
navigation: { en: 'Portal', ar: 'البوابة' },
|
|
301
|
+
myEnrollments: { en: 'My enrollments', ar: 'تسجيلاتي' },
|
|
302
|
+
'greeting.morning': { en: 'Good morning, {name}', ar: 'صباح الخير يا {name}' },
|
|
303
|
+
'greeting.afternoon': { en: 'Good afternoon, {name}', ar: 'مساء الخير يا {name}' },
|
|
304
|
+
'greeting.evening': { en: 'Good evening, {name}', ar: 'مساء الخير يا {name}' },
|
|
305
|
+
yourNext: { en: 'Your next session', ar: 'جلستك القادمة' },
|
|
306
|
+
startsIn: { en: 'Starts {when}', ar: 'تبدأ {when}' },
|
|
307
|
+
viewCourse: { en: 'View course', ar: 'عرض الدورة' },
|
|
308
|
+
canJoin: { en: 'Courses you can join', ar: 'دورات يمكنك الانضمام إليها' },
|
|
309
|
+
seeAll: { en: 'See all courses', ar: 'كل الدورات' },
|
|
310
|
+
noneToJoin: { en: 'No course has a session coming up just now.', ar: 'لا توجد دورة لها جلسة قادمة الآن.' },
|
|
311
|
+
search: { en: 'Search courses', ar: 'ابحث في الدورات' },
|
|
312
|
+
byTag: { en: 'Filter by tag', ar: 'التصفية حسب الوسم' },
|
|
313
|
+
allTags: { en: 'All', ar: 'الكل' },
|
|
314
|
+
noMatch: { en: 'No course matches.', ar: 'لا توجد دورة مطابقة.' },
|
|
315
|
+
next: { en: 'Next session', ar: 'الجلسة القادمة' },
|
|
316
|
+
noSession: { en: 'No session scheduled yet', ar: 'لا توجد جلسة مجدولة بعد' },
|
|
317
|
+
free: { en: 'Free', ar: 'مجانا' },
|
|
318
|
+
perSeat: { en: 'per seat', ar: 'لكل مقعد' },
|
|
319
|
+
back: { en: 'Back to courses', ar: 'العودة إلى الدورات' },
|
|
320
|
+
aboutCourse: { en: 'About this course', ar: 'عن هذه الدورة' },
|
|
321
|
+
choose: { en: 'Choose a session', ar: 'اختر جلسة' },
|
|
322
|
+
full: { en: 'Full', ar: 'مكتملة' },
|
|
323
|
+
seatsLeft_zero: { en: '{count} seats left', ar: 'لم يبق أي مقعد' },
|
|
324
|
+
seatsLeft_one: { en: '{count} seat left', ar: 'بقي مقعد واحد' },
|
|
325
|
+
seatsLeft_two: { en: '{count} seats left', ar: 'بقي مقعدان' },
|
|
326
|
+
seatsLeft_few: { en: '{count} seats left', ar: 'بقيت {count} مقاعد' },
|
|
327
|
+
seatsLeft_many: { en: '{count} seats left', ar: 'بقي {count} مقعدا' },
|
|
328
|
+
seatsLeft_other: { en: '{count} seats left', ar: 'بقي {count} مقعد' },
|
|
329
|
+
total: { en: 'Total', ar: 'الإجمالي' },
|
|
330
|
+
pick: { en: 'Pick a session', ar: 'اختر جلسة' },
|
|
331
|
+
enrollIn: { en: 'Enroll on {when}', ar: 'سجّلني في {when}' },
|
|
332
|
+
enrolled: { en: 'You are enrolled.', ar: 'تم تسجيلك.' },
|
|
333
|
+
enrollFailed: { en: 'The seat was not taken. Please try again.', ar: 'لم يُحجز المقعد. حاول مرة أخرى.' },
|
|
334
|
+
holding: { en: 'You hold a seat on {when}.', ar: 'لك مقعد في {when}.' },
|
|
335
|
+
askSchool: { en: 'Ask your school to enroll you.', ar: 'اطلب من مدرستك تسجيلك.' },
|
|
336
|
+
upcoming: { en: 'Upcoming', ar: 'القادمة' },
|
|
337
|
+
past: { en: 'Past', ar: 'السابقة' },
|
|
338
|
+
noneUpcoming: { en: 'No session to come.', ar: 'لا توجد جلسة قادمة.' },
|
|
339
|
+
nonePast: { en: 'No past session.', ar: 'لا توجد جلسة سابقة.' },
|
|
340
|
+
course: { en: 'Course', ar: 'الدورة' },
|
|
341
|
+
place: { en: 'Venue and format', ar: 'المكان والصيغة' },
|
|
342
|
+
leave: { en: 'Cancel enrollment', ar: 'إلغاء التسجيل' },
|
|
343
|
+
leaveTitle: { en: 'Cancel this enrollment?', ar: 'إلغاء هذا التسجيل؟' },
|
|
344
|
+
leaveBody: { en: 'Your seat goes back to the session, and someone else may take it.', ar: 'يعود مقعدك إلى الجلسة، وقد يأخذه غيرك.' },
|
|
345
|
+
keep: { en: 'Keep it', ar: 'أبقِه' },
|
|
346
|
+
left: { en: 'Your enrollment is cancelled.', ar: 'أُلغي تسجيلك.' },
|
|
347
|
+
leaveFailed: { en: 'The enrollment was not cancelled. Please try again.', ar: 'لم يُلغَ التسجيل. حاول مرة أخرى.' },
|
|
348
|
+
failed: { en: 'This could not be read just now.', ar: 'تعذرت قراءة هذا الآن.' },
|
|
349
|
+
retry: { en: 'Try again', ar: 'حاول مرة أخرى' },
|
|
350
|
+
// end add-on: courses
|
|
351
|
+
// add-on: courses, settings
|
|
352
|
+
closed: { en: 'Closed for maintenance', ar: 'مغلقة للصيانة' },
|
|
353
|
+
closedBody: { en: 'The school is working on the portal. Please come back soon.', ar: 'تعمل المدرسة على البوابة. عد قريبا من فضلك.' },
|
|
354
|
+
contact: { en: 'Questions? Write to', ar: 'لديك سؤال؟ راسل' },
|
|
355
|
+
// end add-on: courses, settings
|
|
356
|
+
},
|
|
357
|
+
auth: {
|
|
358
|
+
signIn: { en: 'Sign in', ar: 'تسجيل الدخول' },
|
|
359
|
+
signInDescription: { en: 'Welcome back. Sign in with your email and password.', ar: 'مرحبا بعودتك. سجّل الدخول ببريدك الإلكتروني وكلمة المرور.' },
|
|
360
|
+
signInBackofficeDescription: { en: 'Sign in to reach the backoffice.', ar: 'سجل الدخول للوصول إلى لوحة الإدارة الخلفية.' },
|
|
361
|
+
signInPending: { en: 'Signing in…', ar: 'جارٍ تسجيل الدخول…' },
|
|
362
|
+
createFirstUser: { en: 'Create the first administrator', ar: 'إنشاء أول مسؤول' },
|
|
363
|
+
createFirstUserDescription: {
|
|
364
|
+
en: 'No one administers this backoffice yet. The account made here becomes its first administrator.',
|
|
365
|
+
ar: 'لا أحد يدير لوحة الإدارة الخلفية هذه بعد. الحساب الذي يُنشأ هنا يصبح أول مسؤول عنها.',
|
|
366
|
+
},
|
|
367
|
+
createFirstUserSubmit: { en: 'Create the administrator', ar: 'إنشاء المسؤول' },
|
|
368
|
+
createFirstUserPending: { en: 'Creating…', ar: 'جارٍ الإنشاء…' },
|
|
369
|
+
createFirstUserFailed: { en: 'That administrator could not be created.', ar: 'تعذر إنشاء هذا المسؤول.' },
|
|
370
|
+
setupCode: { en: 'Setup code', ar: 'رمز الإعداد' },
|
|
371
|
+
setupCodeDescription: { en: 'The SETUP_CODE this server was started with.', ar: 'قيمة SETUP_CODE التي شُغّل بها هذا الخادم.' },
|
|
372
|
+
setupCodeWrong: { en: 'That is not the setup code.', ar: 'هذا ليس رمز الإعداد.' },
|
|
373
|
+
setupCodeUnset: {
|
|
374
|
+
en: 'The first administrator is made only with a setup code, and this server has none it can use: SETUP_CODE is unset or shorter than 32 characters. Set SETUP_CODE where the backoffice app runs to a random value of 32 characters or more, restart it, and open this page again.',
|
|
375
|
+
ar: 'لا يُنشأ أول مسؤول إلا برمز إعداد، وليس لدى هذا الخادم رمز يمكنه استعماله: SETUP_CODE غير مضبوط أو أقصر من 32 حرفا. اضبط SETUP_CODE حيث يعمل تطبيق لوحة الإدارة الخلفية على قيمة عشوائية من 32 حرفا أو أكثر، ثم أعد تشغيله وافتح هذه الصفحة مرة أخرى.',
|
|
376
|
+
},
|
|
377
|
+
firstRegisterClosed: {
|
|
378
|
+
en: 'This backoffice has its administrator, so this page is closed. Sign in instead.',
|
|
379
|
+
ar: 'للوحة الإدارة الخلفية هذه مسؤولها، لذا أُغلقت هذه الصفحة. سجل الدخول بدلا من ذلك.',
|
|
380
|
+
},
|
|
381
|
+
firstRun: { en: 'Set up this app', ar: 'إعداد هذا التطبيق' },
|
|
382
|
+
firstRunDescription: {
|
|
383
|
+
en: 'Nobody owns this app yet. The account made here becomes its owner and first administrator.',
|
|
384
|
+
ar: 'لا أحد يملك هذا التطبيق بعد. الحساب الذي يُنشأ هنا يصبح مالكه وأول مسؤول عنه.',
|
|
385
|
+
},
|
|
386
|
+
firstRunSubmit: { en: 'Set up the app', ar: 'إعداد التطبيق' },
|
|
387
|
+
firstRunCodeUnset: {
|
|
388
|
+
en: 'This app is set up only with a setup code, and this server has none it can use: SETUP_CODE is unset or shorter than 32 characters. Set SETUP_CODE where the app runs to a random value of 32 characters or more, restart it, and open this page again.',
|
|
389
|
+
ar: 'لا يُعدّ هذا التطبيق إلا برمز إعداد، وليس لدى هذا الخادم رمز يمكنه استعماله: SETUP_CODE غير مضبوط أو أقصر من 32 حرفا. اضبط SETUP_CODE حيث يعمل التطبيق على قيمة عشوائية من 32 حرفا أو أكثر، ثم أعد تشغيله وافتح هذه الصفحة مرة أخرى.',
|
|
390
|
+
},
|
|
391
|
+
firstRunPending: { en: 'Setting up…', ar: 'جارٍ الإعداد…' },
|
|
392
|
+
firstRunFailed: { en: 'That owner could not be made.', ar: 'تعذر إنشاء هذا المالك.' },
|
|
393
|
+
firstRunClosed: { en: 'This app has its owner, so this page is closed. Sign in instead.', ar: 'لهذا التطبيق مالكه، لذا أُغلقت هذه الصفحة. سجل الدخول بدلا من ذلك.' },
|
|
394
|
+
setupPending: { en: 'This app is not set up yet. Its administrator sets it up first.', ar: 'لم يُعدّ هذا التطبيق بعد. يُعدّه مسؤوله أولا.' },
|
|
395
|
+
inviteOnly: {
|
|
396
|
+
en: 'Accounts here are made by invitation. Open the link of the invitation sent to you, or sign in.',
|
|
397
|
+
ar: 'تُنشأ الحسابات هنا بدعوة. افتح رابط الدعوة المرسلة إليك، أو سجل الدخول.',
|
|
398
|
+
},
|
|
399
|
+
outsideDomains: { en: 'This email address is not at a domain that may sign up here.', ar: 'هذا البريد الإلكتروني ليس من نطاق يُسمح له بإنشاء حساب هنا.' },
|
|
400
|
+
verifySent: { en: 'Check your email', ar: 'تحقق من بريدك الإلكتروني' },
|
|
401
|
+
verifySentDescription: {
|
|
402
|
+
en: 'We sent a link to {email}. Open it to confirm your address and choose your password; it signs you in. It lasts an hour.',
|
|
403
|
+
ar: 'أرسلنا رابطا إلى {email}. افتحه لتأكيد عنوانك واختيار كلمة المرور، فتدخل به مباشرة. وهو صالح ساعة.',
|
|
404
|
+
},
|
|
405
|
+
failed: { en: 'That sign-in did not work.', ar: 'لم ينجح تسجيل الدخول.' },
|
|
406
|
+
signUp: { en: 'Sign up', ar: 'إنشاء حساب' },
|
|
407
|
+
signUpDescription: { en: 'Make an account.', ar: 'أنشئ حسابا.' },
|
|
408
|
+
signUpPending: { en: 'Signing up…', ar: 'جارٍ إنشاء الحساب…' },
|
|
409
|
+
signUpFailed: { en: 'That sign-up did not work.', ar: 'لم ينجح إنشاء الحساب.' },
|
|
410
|
+
organizationFailed: { en: 'That organization could not be created.', ar: 'تعذر إنشاء هذه المؤسسة.' },
|
|
411
|
+
name: { en: 'Name', ar: 'الاسم' },
|
|
412
|
+
organization: { en: 'Organization', ar: 'المؤسسة' },
|
|
413
|
+
email: { en: 'Email', ar: 'البريد الإلكتروني' },
|
|
414
|
+
password: { en: 'Password', ar: 'كلمة المرور' },
|
|
415
|
+
confirmPassword: { en: 'Confirm the password', ar: 'أكد كلمة المرور' },
|
|
416
|
+
passwordsDiffer: { en: 'The two passwords are not the same.', ar: 'كلمتا المرور غير متطابقتين.' },
|
|
417
|
+
required: { en: 'This field is required.', ar: 'هذا الحقل مطلوب.' },
|
|
418
|
+
invalidEmail: { en: 'Invalid email', ar: 'بريد إلكتروني غير صالح' },
|
|
419
|
+
passwordTooShort: { en: 'Password too short', ar: 'كلمة المرور قصيرة جدا' },
|
|
420
|
+
passwordTooLong: { en: 'Password too long', ar: 'كلمة المرور طويلة جدا' },
|
|
421
|
+
passwordRule: { en: 'At least {count} characters.', ar: '{count} أحرف على الأقل.' },
|
|
422
|
+
showPassword: { en: 'Show password', ar: 'إظهار كلمة المرور' },
|
|
423
|
+
forgotPasswordQuestion: { en: 'Forgot password?', ar: 'هل نسيت كلمة المرور؟' },
|
|
424
|
+
forgotPassword: { en: 'Forgot password', ar: 'نسيت كلمة المرور' },
|
|
425
|
+
forgotPasswordInstructions: {
|
|
426
|
+
en: 'Please enter your email below. You will receive an email message with instructions on how to reset your password.',
|
|
427
|
+
ar: 'أدخل بريدك الإلكتروني أدناه. ستصلك رسالة بريد إلكتروني فيها تعليمات إعادة تعيين كلمة المرور.',
|
|
428
|
+
},
|
|
429
|
+
submit: { en: 'Submit', ar: 'إرسال' },
|
|
430
|
+
emailSent: { en: 'Email sent', ar: 'تم إرسال البريد الإلكتروني' },
|
|
431
|
+
checkYourEmail: {
|
|
432
|
+
en: "If the email address is associated with an account, you will receive instructions to reset your password shortly. Please check your spam or junk mail folder if you don't see the email in your inbox.",
|
|
433
|
+
ar: 'إذا كان هذا البريد الإلكتروني مرتبطا بحساب، فستصلك قريبا تعليمات إعادة تعيين كلمة المرور. إذا لم تجد الرسالة في صندوق الوارد، فتحقق من مجلد البريد العشوائي أو غير المرغوب فيه.',
|
|
434
|
+
},
|
|
435
|
+
noMailer: {
|
|
436
|
+
en: 'No mailer is configured, so no email was sent. In development the reset link is written to the server log.',
|
|
437
|
+
ar: 'لم يُضبط أي مرسل بريد، لذا لم تُرسل أي رسالة. في بيئة التطوير يُكتب رابط إعادة التعيين في سجل الخادم.',
|
|
438
|
+
},
|
|
439
|
+
backToSignIn: { en: 'Back to login', ar: 'العودة إلى تسجيل الدخول' },
|
|
440
|
+
resetPassword: { en: 'Reset password', ar: 'إعادة تعيين كلمة المرور' },
|
|
441
|
+
setPassword: { en: 'Choose your password', ar: 'اختر كلمة المرور' },
|
|
442
|
+
organizationNameIn: { en: 'Its name in {language}, if it has one', ar: 'اسمها بـ{language}، إن كان لها اسم بها' },
|
|
443
|
+
setPasswordSubmit: { en: 'Confirm and set password', ar: 'أكّد واحفظ كلمة المرور' },
|
|
444
|
+
passwordChosen: { en: 'Your address is confirmed and your password is set. Sign in with it.', ar: 'تأكّد عنوانك وحُفظت كلمة المرور. سجّل الدخول بها.' },
|
|
445
|
+
newPassword: { en: 'New password', ar: 'كلمة مرور جديدة' },
|
|
446
|
+
passwordReset: { en: 'Password reset successfully.', ar: 'تمت إعادة تعيين كلمة المرور بنجاح.' },
|
|
447
|
+
tokenInvalid: { en: 'Token is either invalid or has expired.', ar: 'الرمز غير صالح أو انتهت صلاحيته.' },
|
|
448
|
+
alreadySignedIn: { en: 'You are already signed in', ar: 'سجلت الدخول بالفعل' },
|
|
449
|
+
backTo: { en: 'Back to {screen}', ar: 'العودة إلى {screen}' },
|
|
450
|
+
continue: { en: 'Continue', ar: 'متابعة' },
|
|
451
|
+
resetDisabled: {
|
|
452
|
+
en: 'Password reset is not available. Ask your administrator to reset your password.',
|
|
453
|
+
ar: 'إعادة تعيين كلمة المرور غير متاحة. اطلب من المسؤول إعادة تعيين كلمة المرور الخاصة بك.',
|
|
454
|
+
},
|
|
455
|
+
tooManyRequests: { en: 'Too many attempts. Wait a moment and try again.', ar: 'محاولات كثيرة جدا. انتظر قليلا ثم حاول مرة أخرى.' },
|
|
456
|
+
invalidCredentials: { en: 'The email or password provided is incorrect.', ar: 'البريد الإلكتروني أو كلمة المرور غير صحيحة.' },
|
|
457
|
+
emailNotVerified: {
|
|
458
|
+
en: 'This email address is not confirmed yet. Open the link we sent to it, or ask for a password reset to get a new one.',
|
|
459
|
+
ar: 'لم يُؤكَّد هذا البريد الإلكتروني بعد. افتح الرابط الذي أرسلناه إليه، أو اطلب إعادة تعيين كلمة المرور لتحصل على رابط جديد.',
|
|
460
|
+
},
|
|
461
|
+
banned: { en: 'This account is banned, so it cannot sign in.', ar: 'هذا الحساب محظور، لذا لا يمكنه تسجيل الدخول.' },
|
|
462
|
+
userExists: { en: 'An account with this email already exists. Use another email.', ar: 'يوجد حساب بهذا البريد الإلكتروني. استخدم بريدا آخر.' },
|
|
463
|
+
requestFailed: { en: 'That request did not work.', ar: 'لم ينجح هذا الطلب.' },
|
|
464
|
+
resetFailed: { en: 'That reset did not work.', ar: 'لم تنجح إعادة التعيين.' },
|
|
465
|
+
noAccount: { en: 'No account yet?', ar: 'لا يوجد حساب بعد؟' },
|
|
466
|
+
invitation: { en: 'Invitation', ar: 'دعوة' },
|
|
467
|
+
invitationSignedOut: {
|
|
468
|
+
en: 'Sign in, or make an account with the email address the invitation was sent to, to accept it.',
|
|
469
|
+
ar: 'سجل الدخول، أو أنشئ حسابا بالبريد الإلكتروني الذي أُرسلت إليه الدعوة، لتقبلها.',
|
|
470
|
+
},
|
|
471
|
+
invitationReading: { en: 'Reading the invitation…', ar: 'جارٍ قراءة الدعوة…' },
|
|
472
|
+
invitationJoin: { en: 'You are invited to join {organization}.', ar: 'أنت مدعو للانضمام إلى {organization}.' },
|
|
473
|
+
invitationAccept: { en: 'Accept the invitation', ar: 'قبول الدعوة' },
|
|
474
|
+
invitationAccepting: { en: 'Accepting…', ar: 'جارٍ القبول…' },
|
|
475
|
+
invitationAcceptFailed: { en: 'That invitation could not be accepted.', ar: 'تعذر قبول هذه الدعوة.' },
|
|
476
|
+
invitationMissing: { en: 'This link names no invitation.', ar: 'هذا الرابط لا يشير إلى أي دعوة.' },
|
|
477
|
+
invitationInviterGone: {
|
|
478
|
+
en: 'Whoever sent this invitation is no longer a member of the organization, so it cannot be accepted. Ask a member to invite you again.',
|
|
479
|
+
ar: 'من أرسل هذه الدعوة لم يعد عضوا في المؤسسة، لذا لا يمكن قبولها. اطلب من أحد الأعضاء أن يدعوك مرة أخرى.',
|
|
480
|
+
},
|
|
481
|
+
invitationGone: {
|
|
482
|
+
en: 'This invitation has expired, was cancelled or was already accepted. Ask whoever invited you to send a new one.',
|
|
483
|
+
ar: 'انتهت صلاحية هذه الدعوة أو أُلغيت أو قُبلت من قبل. اطلب ممن دعاك أن يرسل دعوة جديدة.',
|
|
484
|
+
},
|
|
485
|
+
invitationNotYours: {
|
|
486
|
+
en: 'This invitation was sent to another email address. Sign out, then sign in with that address to accept it.',
|
|
487
|
+
ar: 'أُرسلت هذه الدعوة إلى بريد إلكتروني آخر. سجل الخروج، ثم سجل الدخول بذلك البريد لقبولها.',
|
|
488
|
+
},
|
|
489
|
+
signUpInvitedDescription: {
|
|
490
|
+
en: 'Make an account with the email address the invitation was sent to.',
|
|
491
|
+
ar: 'أنشئ حسابا بالبريد الإلكتروني الذي أُرسلت إليه الدعوة.',
|
|
492
|
+
},
|
|
493
|
+
haveAccount: { en: 'Already have an account?', ar: 'لديك حساب بالفعل؟' },
|
|
494
|
+
unauthorized: { en: 'Choose where to go', ar: 'اختر وجهتك' },
|
|
495
|
+
unauthorizedDescription: { en: 'This page isn\'t part of your account. Where would you like to go?', ar: 'هذه الصفحة ليست ضمن حسابك. إلى أين تريد الذهاب؟' },
|
|
496
|
+
unauthorizedPage: { en: 'Your role does not open that page: {page}.', ar: 'دورك لا يفتح تلك الصفحة: {page}.' },
|
|
497
|
+
noAccess: { en: 'Your account has no access yet', ar: 'لا يملك حسابك أي وصول بعد' },
|
|
498
|
+
noAccessDescription: {
|
|
499
|
+
en: '{email} is a member of {organization} and holds no role that opens a screen here. Ask an administrator of {organization} to give you one.',
|
|
500
|
+
ar: '{email} عضو في {organization} ولا يملك دورا يفتح أي شاشة هنا. اطلب من أحد مسؤولي {organization} أن يمنحك دورا.',
|
|
501
|
+
},
|
|
502
|
+
switchTo: { en: 'Switch to {organization}', ar: 'التبديل إلى {organization}' },
|
|
503
|
+
accountStatus: { en: 'See your account status', ar: 'اطّلع على حالة حسابك' },
|
|
504
|
+
noOrganization: { en: '{email} belongs to no organization yet.', ar: '{email} لا ينتمي إلى أي مؤسسة بعد.' },
|
|
505
|
+
yourInvitations: { en: 'Your invitations', ar: 'دعواتك' },
|
|
506
|
+
invitationDecline: { en: 'Decline', ar: 'رفض' },
|
|
507
|
+
createOrganization: { en: 'Create an organization', ar: 'أنشئ مؤسسة' },
|
|
508
|
+
createOrganizationSubmit: { en: 'Create', ar: 'إنشاء' },
|
|
509
|
+
workspaceOf: { en: '{name}\'s workspace', ar: 'مساحة عمل {name}' },
|
|
510
|
+
askToBeInvited: { en: 'Ask an administrator to invite {email}.', ar: 'اطلب من أحد المسؤولين أن يدعو {email}.' },
|
|
511
|
+
},
|
|
512
|
+
marketing: {
|
|
513
|
+
brand: { en: 'Asas', ar: 'أساس' },
|
|
514
|
+
getStarted: { en: 'Get started', ar: 'ابدأ الآن' },
|
|
515
|
+
openScreen: { en: 'Open {screen}', ar: 'افتح {screen}' },
|
|
516
|
+
heroTitle: { en: 'Run your organization without the busywork', ar: 'أدر مؤسستك دون العمل الروتيني' },
|
|
517
|
+
heroSubtitle: {
|
|
518
|
+
en: 'Asas is a dashboard and a portal for organizations: keep your records in one place, and keep every role to what it should see.',
|
|
519
|
+
ar: 'أساس لوحة تحكم وبوابة للمؤسسات: احفظ سجلاتك في مكان واحد، واحصر كل دور بما ينبغي أن يراه.',
|
|
520
|
+
},
|
|
521
|
+
featuresTitle: { en: 'Everything an organization needs', ar: 'كل ما تحتاجه مؤسسة' },
|
|
522
|
+
featuresSubtitle: {
|
|
523
|
+
en: 'One system for your records, from the first entry to who may see it.',
|
|
524
|
+
ar: 'نظام واحد لسجلاتك، من أول إدخال إلى من يحق له رؤيته.',
|
|
525
|
+
},
|
|
526
|
+
featureRolesTitle: { en: 'Roles and permissions', ar: 'الأدوار والصلاحيات' },
|
|
527
|
+
featureRolesDescription: {
|
|
528
|
+
en: 'Each organization sets its own roles, and every screen, field and action follows what a role may read or write.',
|
|
529
|
+
ar: 'تحدد كل مؤسسة أدوارها الخاصة، وتتبع كل شاشة وحقل وإجراء ما يحق للدور قراءته أو تعديله.',
|
|
530
|
+
},
|
|
531
|
+
featureBilingualTitle: { en: 'Arabic and English, either direction', ar: 'العربية والإنجليزية، بكلا الاتجاهين' },
|
|
532
|
+
featureBilingualDescription: {
|
|
533
|
+
en: 'Every screen and every label is written in both languages, and the layout mirrors correctly under Arabic.',
|
|
534
|
+
ar: 'كل شاشة وكل تسمية مكتوبة باللغتين، ويُعكس التخطيط بشكل صحيح تحت العربية.',
|
|
535
|
+
},
|
|
536
|
+
featurePortalTitle: { en: 'A dashboard and a customer portal', ar: 'لوحة تحكم وبوابة للعملاء' },
|
|
537
|
+
featurePortalDescription: {
|
|
538
|
+
en: 'Staff work from the dashboard; customers see their own shorter view of the same data in the portal.',
|
|
539
|
+
ar: 'يعمل الموظفون من لوحة التحكم؛ ويرى العملاء نسختهم المختصرة من البيانات نفسها في البوابة.',
|
|
540
|
+
},
|
|
541
|
+
howTitle: { en: 'How it works', ar: 'كيف يعمل' },
|
|
542
|
+
howStep1Title: { en: 'Set up your organization', ar: 'أنشئ مؤسستك' },
|
|
543
|
+
howStep1Description: { en: 'Sign up and create your organization in one step.', ar: 'أنشئ حسابك ومؤسستك في خطوة واحدة.' },
|
|
544
|
+
howStep2Title: { en: 'Add your records and roles', ar: 'أضف سجلاتك وأدوارك' },
|
|
545
|
+
howStep2Description: {
|
|
546
|
+
en: 'List what your organization keeps and decide what each role may do.',
|
|
547
|
+
ar: 'أدرج ما تحفظه مؤسستك وحدد ما يحق لكل دور القيام به.',
|
|
548
|
+
},
|
|
549
|
+
howStep3Title: { en: 'Open the portal to your customers', ar: 'افتح البوابة لعملائك' },
|
|
550
|
+
howStep3Description: {
|
|
551
|
+
en: 'Customers sign in to their own portal to see what is theirs.',
|
|
552
|
+
ar: 'يسجل العملاء الدخول إلى بوابتهم الخاصة لرؤية ما يخصهم.',
|
|
553
|
+
},
|
|
554
|
+
ctaTitle: { en: 'Start running your organization on Asas', ar: 'ابدأ بإدارة مؤسستك على أساس' },
|
|
555
|
+
ctaSubtitle: { en: 'Create your organization and add your first record in minutes.', ar: 'أنشئ مؤسستك وأضف أول سجل لك خلال دقائق.' },
|
|
556
|
+
footer: { en: 'Asas — one place for an organization\'s records.', ar: 'أساس — مكان واحد لسجلات المؤسسة.' },
|
|
557
|
+
},
|
|
558
|
+
dashboard: {
|
|
559
|
+
dashboard: { en: 'Dashboard', ar: 'لوحة التحكم' },
|
|
560
|
+
openBackoffice: { en: 'Open the backoffice', ar: 'فتح لوحة الإدارة الخلفية' },
|
|
561
|
+
members: { en: 'Members', ar: 'الأعضاء' },
|
|
562
|
+
membersAbout: { en: 'Who works in this organization, and the role each holds.', ar: 'من يعمل في هذه المؤسسة، والدور الذي يشغله كل منهم.' },
|
|
563
|
+
membersRoles: { en: 'Roles', ar: 'الأدوار' },
|
|
564
|
+
membersName: { en: 'Name', ar: 'الاسم' },
|
|
565
|
+
membersEmail: { en: 'Email', ar: 'البريد الإلكتروني' },
|
|
566
|
+
membersJoined: { en: 'Joined', ar: 'تاريخ الانضمام' },
|
|
567
|
+
membersNone: { en: 'No one works in this organization yet.', ar: 'لا يعمل أحد في هذه المؤسسة بعد.' },
|
|
568
|
+
invite: { en: 'Invite', ar: 'دعوة' },
|
|
569
|
+
addPerson: { en: 'Add person', ar: 'إضافة شخص' },
|
|
570
|
+
addPersonTitle: { en: 'Add a person', ar: 'إضافة شخص' },
|
|
571
|
+
addPersonAbout: {
|
|
572
|
+
en: 'They get an account here and a link by email to choose their password. Someone who already has an account is sent an invitation instead.',
|
|
573
|
+
ar: 'يحصل على حساب هنا ورابط بالبريد لاختيار كلمة مروره. ومن لديه حساب بالفعل تُرسل إليه دعوة بدلا من ذلك.',
|
|
574
|
+
},
|
|
575
|
+
addPersonRole: { en: 'Role', ar: 'الدور' },
|
|
576
|
+
addPersonNoRole: { en: 'No role yet', ar: 'بلا دور بعد' },
|
|
577
|
+
addPersonSubmit: { en: 'Add', ar: 'إضافة' },
|
|
578
|
+
addPersonLink: {
|
|
579
|
+
en: 'No mailer is configured. Hand this link to them alone: it chooses their password and lasts 48 hours.',
|
|
580
|
+
ar: 'لم يُضبط أي مرسل بريد. سلّم هذا الرابط له وحده: به يختار كلمة مروره، وهو صالح 48 ساعة.',
|
|
581
|
+
},
|
|
582
|
+
addPersonRefused: { en: 'The server refused to add that person.', ar: 'رفض الخادم إضافة هذا الشخص.' },
|
|
583
|
+
inviteTitle: { en: 'Invite someone', ar: 'دعوة شخص' },
|
|
584
|
+
inviteAbout: {
|
|
585
|
+
en: 'They join this organization as a member once they accept. Give them roles here after that.',
|
|
586
|
+
ar: 'ينضم إلى هذه المؤسسة عضوًا بعد أن يقبل الدعوة. امنحه الأدوار هنا بعد ذلك.',
|
|
587
|
+
},
|
|
588
|
+
inviteSend: { en: 'Send invitation', ar: 'إرسال الدعوة' },
|
|
589
|
+
inviteRefused: { en: 'The server refused that invitation.', ar: 'رفض الخادم هذه الدعوة.' },
|
|
590
|
+
inviteSent: { en: 'Invitation sent to {email}', ar: 'أُرسلت الدعوة إلى {email}' },
|
|
591
|
+
inviteCancelled: { en: 'Invitation cancelled', ar: 'أُلغيت الدعوة' },
|
|
592
|
+
rolesSaved: { en: 'Roles saved for {name}', ar: 'حُفظت أدوار {name}' },
|
|
593
|
+
memberRemoved: { en: '{name} removed', ar: 'أُزيل {name}' },
|
|
594
|
+
personAdded: { en: '{name} added. A link to choose a password was sent to {email}.', ar: 'أُضيف {name}. أُرسل إلى {email} رابط لاختيار كلمة المرور.' },
|
|
595
|
+
personAddedLink: { en: '{name} added. Hand them the link below.', ar: 'أُضيف {name}. سلّمه الرابط أدناه.' },
|
|
596
|
+
inviteAlreadyMember: { en: 'This person is already a member of this organization.', ar: 'هذا الشخص عضو في هذه المؤسسة بالفعل.' },
|
|
597
|
+
inviteAlreadyInvited: { en: 'This person already has an invitation that is not yet accepted.', ar: 'لدى هذا الشخص دعوة لم تُقبل بعد.' },
|
|
598
|
+
inviteNotAllowed: { en: 'You may not invite people to this organization.', ar: 'لا يحق لك دعوة أشخاص إلى هذه المؤسسة.' },
|
|
599
|
+
inviteNoMailer: {
|
|
600
|
+
en: 'No mailer is configured, so no email is sent. In development the link is written to the server log.',
|
|
601
|
+
ar: 'لم يُضبط أي مرسل بريد، لذا لا تُرسل أي رسالة. في بيئة التطوير يُكتب الرابط في سجل الخادم.',
|
|
602
|
+
},
|
|
603
|
+
inviteCopy: { en: 'Copy link', ar: 'نسخ الرابط' },
|
|
604
|
+
link: { en: 'Link', ar: 'الرابط' },
|
|
605
|
+
inviteLinkOwn: {
|
|
606
|
+
en: 'A copied link lets whoever holds it join as the invited address: hand it to that person alone.',
|
|
607
|
+
ar: 'الرابط المنسوخ يتيح لمن يحمله الانضمام بالعنوان المدعو: سلّمه لذلك الشخص وحده.',
|
|
608
|
+
},
|
|
609
|
+
inviteCopied: { en: 'Link copied', ar: 'تم نسخ الرابط' },
|
|
610
|
+
inviteCancel: { en: 'Cancel invitation', ar: 'إلغاء الدعوة' },
|
|
611
|
+
inviteCancelRefused: { en: 'The server refused to cancel that invitation.', ar: 'رفض الخادم إلغاء هذه الدعوة.' },
|
|
612
|
+
inviteCancelNotAllowed: { en: 'You may not cancel this invitation.', ar: 'لا يحق لك إلغاء هذه الدعوة.' },
|
|
613
|
+
removeMember: { en: 'Remove from organization', ar: 'إزالة من المؤسسة' },
|
|
614
|
+
removeAbout: {
|
|
615
|
+
en: 'They lose access to this organization and every role they hold in it. Inviting them again does not give the roles back.',
|
|
616
|
+
ar: 'يفقد هذا العضو الوصول إلى هذه المؤسسة وكل الأدوار التي يشغلها فيها. دعوته مرة أخرى لا تعيد إليه أدواره.',
|
|
617
|
+
},
|
|
618
|
+
removeConfirm: { en: 'Remove for good', ar: 'إزالة نهائيا' },
|
|
619
|
+
removeKeep: { en: 'Keep this member', ar: 'إبقاء هذا العضو' },
|
|
620
|
+
removeRefused: { en: 'The server refused to remove that member.', ar: 'رفض الخادم إزالة هذا العضو.' },
|
|
621
|
+
removeLastOwner: { en: 'The organization\'s only owner cannot be removed.', ar: 'لا يمكن إزالة المالك الوحيد للمؤسسة.' },
|
|
622
|
+
removeNotAllowed: { en: 'You may not remove members of this organization.', ar: 'لا يحق لك إزالة أعضاء هذه المؤسسة.' },
|
|
623
|
+
rolesHolderUnnamed: { en: 'A member', ar: 'أحد الأعضاء' },
|
|
624
|
+
invitationsTab: { en: 'Invitations ({count})', ar: 'الدعوات ({count})' },
|
|
625
|
+
invitesNone: { en: 'No invitation is waiting to be accepted.', ar: 'لا توجد دعوة تنتظر القبول.' },
|
|
626
|
+
inviteByLink: { en: 'Invite by link', ar: 'الدعوة برابط' },
|
|
627
|
+
inviteSentBy: { en: 'Sent by', ar: 'أرسلها' },
|
|
628
|
+
inviteExpired: { en: 'Expired', ar: 'منتهية' },
|
|
629
|
+
inviteResend: { en: 'Resend', ar: 'إعادة الإرسال' },
|
|
630
|
+
inviteResent: { en: 'Invitation sent again to {email}', ar: 'أُعيد إرسال الدعوة إلى {email}' },
|
|
631
|
+
membersRoleFilter: { en: 'Role', ar: 'الدور' },
|
|
632
|
+
membersEveryRole: { en: 'Every role', ar: 'كل الأدوار' },
|
|
633
|
+
membersNotGrantable: {
|
|
634
|
+
en: 'It grants keys you do not hold, so only someone who holds them gives or takes it.',
|
|
635
|
+
ar: 'يمنح صلاحيات لا تملكها، لذا لا يمنحه أو يسحبه إلا من يملكها.',
|
|
636
|
+
},
|
|
637
|
+
passwordLink: { en: 'Send a new password link', ar: 'إرسال رابط جديد لكلمة المرور' },
|
|
638
|
+
passwordLinkSent: { en: 'A link to choose a password was sent to {email}', ar: 'أُرسل إلى {email} رابط لاختيار كلمة المرور' },
|
|
639
|
+
passwordLinkRefused: { en: 'The server refused to send that link.', ar: 'رفض الخادم إرسال هذا الرابط.' },
|
|
640
|
+
passwordLinkHanded: {
|
|
641
|
+
en: 'No mailer is configured. Hand this link to {name} alone: it chooses their password and lasts 48 hours.',
|
|
642
|
+
ar: 'لم يُضبط أي مرسل بريد. سلّم هذا الرابط إلى {name} وحده: به يختار كلمة مروره، وهو صالح 48 ساعة.',
|
|
643
|
+
},
|
|
644
|
+
removeTitle: { en: 'Remove {name}?', ar: 'إزالة {name}؟' },
|
|
645
|
+
inviteExpires: { en: 'Expires', ar: 'تنتهي في' },
|
|
646
|
+
membersEdit: { en: 'Edit roles', ar: 'تعديل الأدوار' },
|
|
647
|
+
membersFixed: { en: 'Fixed roles', ar: 'الأدوار الثابتة' },
|
|
648
|
+
membersCustom: { en: 'Custom roles', ar: 'الأدوار المخصصة' },
|
|
649
|
+
membersNoRoles: { en: 'There is no role you may read to assign.', ar: 'لا يوجد دور يمكنك قراءته لإسناده.' },
|
|
650
|
+
membersRefused: { en: 'The server refused that change.', ar: 'رفض الخادم هذا التغيير.' },
|
|
651
|
+
everyPermission: { en: 'Every permission', ar: 'كل الصلاحيات' },
|
|
652
|
+
rolesAbout: {
|
|
653
|
+
en: 'Fixed roles ship with the project and cannot be changed here. Custom roles are this organization\'s own.',
|
|
654
|
+
ar: 'الأدوار الثابتة تأتي مع المشروع ولا يمكن تغييرها هنا. الأدوار المخصصة خاصة بهذه المؤسسة.',
|
|
655
|
+
},
|
|
656
|
+
rolesNew: { en: 'New role', ar: 'دور جديد' },
|
|
657
|
+
rolesKind: { en: 'Kind', ar: 'النوع' },
|
|
658
|
+
rolesFixed: { en: 'Fixed', ar: 'ثابت' },
|
|
659
|
+
rolesCustom: { en: 'Custom', ar: 'مخصص' },
|
|
660
|
+
rolesOpen: { en: 'Open {role}', ar: 'فتح {role}' },
|
|
661
|
+
rolesFixedAbout: {
|
|
662
|
+
en: 'This role ships with the project. What it holds is code, so it cannot be edited or deleted here.',
|
|
663
|
+
ar: 'يأتي هذا الدور مع المشروع. ما يمنحه مكتوب في الشيفرة، لذا لا يمكن تعديله أو حذفه هنا.',
|
|
664
|
+
},
|
|
665
|
+
rolesDeleteTitle: { en: 'Delete this role?', ar: 'حذف هذا الدور؟' },
|
|
666
|
+
rolesHeldTitle: { en: 'Members hold this role', ar: 'أعضاء يشغلون هذا الدور' },
|
|
667
|
+
rolesHeld: {
|
|
668
|
+
en: 'A role a member holds is not deleted. Take it from each of them on the Members page first.',
|
|
669
|
+
ar: 'لا يُحذف دور يشغله عضو. اسحبه من كل منهم في صفحة الأعضاء أولا.',
|
|
670
|
+
},
|
|
671
|
+
permissionsScreens: { en: 'Screens', ar: 'الشاشات' },
|
|
672
|
+
permissionsFields: { en: 'Fields', ar: 'الحقول' },
|
|
673
|
+
permissionsCount: { en: '{count} permissions', ar: 'الصلاحيات: {count}' },
|
|
674
|
+
permissionsCountOne: { en: '1 permission', ar: 'صلاحية واحدة' },
|
|
675
|
+
permissionsOther: { en: 'Other', ar: 'أخرى' },
|
|
676
|
+
permissionsRequired: { en: 'Permissions, required', ar: 'الصلاحيات، مطلوبة' },
|
|
677
|
+
membersOwnRole: {
|
|
678
|
+
en: 'This role lets you manage roles, so only another member who may manage roles takes it from you.',
|
|
679
|
+
ar: 'هذا الدور يتيح لك إدارة الأدوار، لذا لا يسحبه منك إلا عضو آخر يملك إدارة الأدوار.',
|
|
680
|
+
},
|
|
681
|
+
membersOwnerRole: {
|
|
682
|
+
en: 'The owner of the organization always holds this role.',
|
|
683
|
+
ar: 'مالك المؤسسة يشغل هذا الدور دائما.',
|
|
684
|
+
},
|
|
685
|
+
dashboardAbout: { en: 'What this organization holds, and how many have enrolled lately.', ar: 'ما تضمه هذه المؤسسة، وعدد من سجلوا مؤخرا.' },
|
|
686
|
+
dashboardAboutCatalogue: { en: 'What this organization holds.', ar: 'ما تضمه هذه المؤسسة.' },
|
|
687
|
+
kpiCourses: { en: 'Courses', ar: 'الدورات' },
|
|
688
|
+
kpiCoursesOpen: { en: '{count} not archived', ar: 'غير المؤرشفة: {count}' },
|
|
689
|
+
kpiFees: { en: 'Total fees', ar: 'مجموع الرسوم' },
|
|
690
|
+
kpiFeesFooter: { en: 'The fee of every course not archived, summed per currency', ar: 'رسوم كل دورة غير مؤرشفة، مجموعة لكل عملة' },
|
|
691
|
+
kpiEnrollments: { en: 'Enrollments', ar: 'التسجيلات' },
|
|
692
|
+
kpiAllTime: { en: 'Since the first one', ar: 'منذ أول تسجيل' },
|
|
693
|
+
kpiRecent: { en: 'New enrollments', ar: 'التسجيلات الجديدة' },
|
|
694
|
+
kpiInRange_few: { en: 'In the last {count} days', ar: 'خلال آخر {count} أيام' },
|
|
695
|
+
kpiInRange_many: { en: 'In the last {count} days', ar: 'خلال آخر {count} يومًا' },
|
|
696
|
+
kpiInRange_other: { en: 'In the last {count} days', ar: 'خلال آخر {count} يوم' },
|
|
697
|
+
chartEnrollments: { en: 'Enrollments per day', ar: 'التسجيلات في كل يوم' },
|
|
698
|
+
chartDays_few: { en: '{count} days', ar: '{count} أيام' },
|
|
699
|
+
chartDays_many: { en: '{count} days', ar: '{count} يومًا' },
|
|
700
|
+
chartDays_other: { en: '{count} days', ar: '{count} يوم' },
|
|
701
|
+
upcoming: { en: 'Upcoming sessions', ar: 'الجلسات القادمة' },
|
|
702
|
+
upcomingAbout: { en: 'The next sessions to start, soonest first.', ar: 'الجلسات التالية التي ستبدأ، الأقرب أولا.' },
|
|
703
|
+
upcomingNone: { en: 'No session is about to start.', ar: 'لا توجد جلسة على وشك البدء.' },
|
|
704
|
+
},
|
|
705
|
+
permissions: {
|
|
706
|
+
find: { en: 'List', ar: 'عرض القائمة' },
|
|
707
|
+
findByID: { en: 'View', ar: 'عرض' },
|
|
708
|
+
findOne: { en: 'View', ar: 'عرض' },
|
|
709
|
+
count: { en: 'Count', ar: 'العد' },
|
|
710
|
+
create: { en: 'Create', ar: 'إنشاء' },
|
|
711
|
+
update: { en: 'Edit', ar: 'تعديل' },
|
|
712
|
+
delete: { en: 'Delete', ar: 'حذف' },
|
|
713
|
+
updateMany: { en: 'Edit many', ar: 'تعديل عدة سجلات' },
|
|
714
|
+
deleteMany: { en: 'Delete many', ar: 'حذف عدة سجلات' },
|
|
715
|
+
findVersions: { en: 'List versions', ar: 'عرض الإصدارات' },
|
|
716
|
+
findVersionByID: { en: 'View a version', ar: 'عرض إصدار' },
|
|
717
|
+
restoreVersion: { en: 'Restore a version', ar: 'استعادة إصدار' },
|
|
718
|
+
},
|
|
719
|
+
locale: {
|
|
720
|
+
ar: { en: 'Arabic', ar: 'العربية' },
|
|
721
|
+
en: { en: 'English', ar: 'الإنجليزية' },
|
|
722
|
+
},
|
|
723
|
+
screens: {
|
|
724
|
+
dashboard: { en: 'Dashboard', ar: 'لوحة التحكم' },
|
|
725
|
+
portal: { en: 'Portal', ar: 'البوابة' },
|
|
726
|
+
backoffice: { en: 'Backoffice', ar: 'لوحة الإدارة الخلفية' },
|
|
727
|
+
},
|
|
728
|
+
backoffice: {
|
|
729
|
+
title: { en: 'Backoffice', ar: 'لوحة الإدارة الخلفية' },
|
|
730
|
+
brand: { en: 'Asas backoffice', ar: 'لوحة الإدارة الخلفية لأساس' },
|
|
731
|
+
about: { en: 'Every organization and every person, for the backoffice staff.', ar: 'كل مؤسسة وكل شخص، لفريق لوحة الإدارة الخلفية.' },
|
|
732
|
+
role: { en: 'Backoffice role', ar: 'دور في لوحة الإدارة الخلفية' },
|
|
733
|
+
noRole: { en: 'No backoffice role', ar: 'بلا دور في لوحة الإدارة الخلفية' },
|
|
734
|
+
setRole: { en: 'Set role', ar: 'تعيين الدور' },
|
|
735
|
+
ban: { en: 'Ban', ar: 'حظر' },
|
|
736
|
+
unban: { en: 'Unban', ar: 'رفع الحظر' },
|
|
737
|
+
impersonate: { en: 'Impersonate', ar: 'انتحال الصفة' },
|
|
738
|
+
confirmRole: { en: 'Give {name} the backoffice role "{role}"?', ar: 'منح {name} الدور "{role}" في لوحة الإدارة الخلفية؟' },
|
|
739
|
+
pickRole: { en: 'Pick another backoffice role for {name}.', ar: 'اختر دورًا آخر في لوحة الإدارة الخلفية لـ {name}.' },
|
|
740
|
+
confirmBan: {
|
|
741
|
+
en: 'Ban {name}? They are signed out of both apps and cannot sign in until the ban ends.',
|
|
742
|
+
ar: 'حظر {name}؟ سيُسجَّل خروجه من التطبيقين ولن يتمكن من تسجيل الدخول حتى ينتهي الحظر.',
|
|
743
|
+
},
|
|
744
|
+
confirmUnban: { en: 'Lift the ban on {name}? They can sign in again.', ar: 'رفع الحظر عن {name}؟ سيتمكن من تسجيل الدخول مجددا.' },
|
|
745
|
+
confirmImpersonate: {
|
|
746
|
+
en: 'Impersonate {name}? You act as them in the tenant app until you stop.',
|
|
747
|
+
ar: 'انتحال صفة {name}؟ ستتصرف باسمه في تطبيق المؤسسة حتى تتوقف.',
|
|
748
|
+
},
|
|
749
|
+
roleSet: { en: '{name} now holds the backoffice role "{role}".', ar: 'أصبح دور {name} في لوحة الإدارة الخلفية "{role}".' },
|
|
750
|
+
banned: { en: '{name} is banned.', ar: 'تم حظر {name}.' },
|
|
751
|
+
unbanned: { en: 'The ban on {name} is lifted.', ar: 'تم رفع الحظر عن {name}.' },
|
|
752
|
+
renamed: { en: 'The organization is now named {name}.', ar: 'أصبح اسم المؤسسة {name}.' },
|
|
753
|
+
refusedBanned: { en: 'This person is banned, so they cannot be impersonated.', ar: 'هذا الشخص محظور، فلا يمكن انتحال صفته.' },
|
|
754
|
+
banReason: { en: 'Reason', ar: 'السبب' },
|
|
755
|
+
banReasonNeeded: { en: 'A ban needs a reason.', ar: 'يحتاج الحظر إلى سبب.' },
|
|
756
|
+
banEnds: { en: 'Ban lasts', ar: 'مدة الحظر' },
|
|
757
|
+
banUntilLifted: { en: 'Until it is lifted', ar: 'حتى يُرفع' },
|
|
758
|
+
banDay: { en: 'One day', ar: 'يوم واحد' },
|
|
759
|
+
banWeek: { en: 'Seven days', ar: 'سبعة أيام' },
|
|
760
|
+
banMonth: { en: 'Thirty days', ar: 'ثلاثون يومًا' },
|
|
761
|
+
rename: { en: 'Rename', ar: 'إعادة التسمية' },
|
|
762
|
+
nameIn: { en: 'Name in {language}, if it has one', ar: 'الاسم بـ{language}، إن كان لها اسم بها' },
|
|
763
|
+
slugPattern: {
|
|
764
|
+
en: 'A slug is lowercase letters and digits joined by single dashes, such as admin-org.',
|
|
765
|
+
ar: 'المعرف النصي أحرف إنجليزية صغيرة وأرقام تفصل بينها شرطات مفردة، مثل admin-org.',
|
|
766
|
+
},
|
|
767
|
+
renameAbout: {
|
|
768
|
+
en: 'The new name and slug take the place of the old ones for everyone in this organization.',
|
|
769
|
+
ar: 'يحل الاسم والمعرّف الجديدان محل القديمين لكل من في هذه المؤسسة.',
|
|
770
|
+
},
|
|
771
|
+
members: { en: 'Members', ar: 'الأعضاء' },
|
|
772
|
+
refused: { en: 'The backoffice refused that action.', ar: 'رفضت لوحة الإدارة الخلفية هذا الإجراء.' },
|
|
773
|
+
'membership.owner': { en: 'Owner', ar: 'المالك' },
|
|
774
|
+
'membership.admin': { en: 'Admin', ar: 'مشرف' },
|
|
775
|
+
'membership.member': { en: 'Member', ar: 'عضو' },
|
|
776
|
+
},
|
|
777
|
+
user: {
|
|
778
|
+
singular: { en: 'Person', ar: 'شخص' },
|
|
779
|
+
plural: { en: 'People', ar: 'الأشخاص' },
|
|
780
|
+
none: { en: 'No people yet.', ar: 'لا يوجد أشخاص بعد.' },
|
|
781
|
+
id: { en: 'ID', ar: 'المعرّف' },
|
|
782
|
+
name: { en: 'Name', ar: 'الاسم' },
|
|
783
|
+
email: { en: 'Email', ar: 'البريد الإلكتروني' },
|
|
784
|
+
emailVerified: { en: 'Email verified', ar: 'البريد موثق' },
|
|
785
|
+
image: { en: 'Image', ar: 'الصورة' },
|
|
786
|
+
role: { en: 'Backoffice role', ar: 'دور في لوحة الإدارة الخلفية' },
|
|
787
|
+
banned: { en: 'Banned', ar: 'محظور' },
|
|
788
|
+
banReason: { en: 'Ban reason', ar: 'سبب الحظر' },
|
|
789
|
+
banExpires: { en: 'Ban ends', ar: 'ينتهي الحظر' },
|
|
790
|
+
createdAt: { en: 'Created at', ar: 'تاريخ الإنشاء' },
|
|
791
|
+
updatedAt: { en: 'Updated at', ar: 'تاريخ التعديل' },
|
|
792
|
+
},
|
|
793
|
+
organization: {
|
|
794
|
+
...copyFile(organization),
|
|
795
|
+
singular: { en: 'Organization', ar: 'مؤسسة' },
|
|
796
|
+
plural: { en: 'Organizations', ar: 'المؤسسات' },
|
|
797
|
+
none: { en: 'No organizations yet.', ar: 'لا توجد مؤسسات بعد.' },
|
|
798
|
+
id: { en: 'ID', ar: 'المعرّف' },
|
|
799
|
+
name: { en: 'Name', ar: 'الاسم' },
|
|
800
|
+
slug: { en: 'Slug', ar: 'المعرّف النصي' },
|
|
801
|
+
logo: { en: 'Logo', ar: 'الشعار' },
|
|
802
|
+
metadata: { en: 'Metadata', ar: 'البيانات الوصفية' },
|
|
803
|
+
createdAt: { en: 'Created at', ar: 'تاريخ الإنشاء' },
|
|
804
|
+
updatedAt: { en: 'Updated at', ar: 'تاريخ التعديل' },
|
|
805
|
+
},
|
|
806
|
+
member: {
|
|
807
|
+
singular: { en: 'Member', ar: 'عضو' },
|
|
808
|
+
plural: { en: 'Members', ar: 'الأعضاء' },
|
|
809
|
+
none: { en: 'No members yet.', ar: 'لا يوجد أعضاء بعد.' },
|
|
810
|
+
id: { en: 'ID', ar: 'المعرّف' },
|
|
811
|
+
userId: { en: 'Person', ar: 'الشخص' },
|
|
812
|
+
organizationId: { en: 'Organization', ar: 'المؤسسة' },
|
|
813
|
+
role: { en: 'Membership role', ar: 'دور العضوية' },
|
|
814
|
+
createdAt: { en: 'Created at', ar: 'تاريخ الإنشاء' },
|
|
815
|
+
updatedAt: { en: 'Updated at', ar: 'تاريخ التعديل' },
|
|
816
|
+
lastSeenAt: { en: 'Last seen', ar: 'آخر ظهور' },
|
|
817
|
+
},
|
|
818
|
+
// add-on: settings
|
|
819
|
+
settings: {
|
|
820
|
+
...copyFile(settings),
|
|
821
|
+
singular: { en: 'Settings', ar: 'الإعدادات' },
|
|
822
|
+
plural: { en: 'Settings', ar: 'الإعدادات' },
|
|
823
|
+
},
|
|
824
|
+
// end add-on: settings
|
|
825
|
+
// add-on: roles
|
|
826
|
+
roles: {
|
|
827
|
+
...copyFile(roles),
|
|
828
|
+
singular: { en: 'Role', ar: 'دور' },
|
|
829
|
+
plural: { en: 'Roles', ar: 'الأدوار' },
|
|
830
|
+
'delete.confirm': { en: 'Delete this role', ar: 'احذف هذا الدور' },
|
|
831
|
+
new: { en: 'New role', ar: 'دور جديد' },
|
|
832
|
+
none: { en: 'No roles yet.', ar: 'لا توجد أدوار بعد.' },
|
|
833
|
+
dangerZoneWarning: {
|
|
834
|
+
en: 'Deleting this role takes it away for everyone. A role a member holds is not deleted: take it from each of them on the Members page first.',
|
|
835
|
+
ar: 'حذف هذا الدور يزيله للجميع. لا يُحذف دور يشغله عضو: اسحبه من كل منهم في صفحة الأعضاء أولا.',
|
|
836
|
+
},
|
|
837
|
+
'name.description': {
|
|
838
|
+
en: 'The role\'s slug, which every assignment of it names, such as course-editor. People read the role by its label below.',
|
|
839
|
+
ar: 'المعرف النصي للدور، الذي يسميه كل إسناد له، مثل course-editor. يقرأ الناس الدور بتسميته أدناه.',
|
|
840
|
+
},
|
|
841
|
+
},
|
|
842
|
+
// end add-on: roles
|
|
843
|
+
// add-on: courses
|
|
844
|
+
sessions: {
|
|
845
|
+
...copyFile(sessions),
|
|
846
|
+
singular: { en: 'Session', ar: 'جلسة' },
|
|
847
|
+
plural: { en: 'Sessions', ar: 'الجلسات' },
|
|
848
|
+
'operations.archiveMany': { en: 'Archive many', ar: 'أرشفة عدة جلسات' },
|
|
849
|
+
'operations.archive': { en: 'Archive', ar: 'أرشفة' },
|
|
850
|
+
'operations.archive.reason.required': { en: 'An archive reason is required.', ar: 'سبب الأرشفة مطلوب.' },
|
|
851
|
+
'operations.archive.reason.maxLength': { en: 'An archive reason is at most 200 characters.', ar: 'سبب الأرشفة 200 حرف على الأكثر.' },
|
|
852
|
+
'operations.unarchive': { en: 'Unarchive', ar: 'إلغاء الأرشفة' },
|
|
853
|
+
'operations.unarchive.reason.required': { en: 'A reason to bring it back is required.', ar: 'سبب إلغاء الأرشفة مطلوب.' },
|
|
854
|
+
'operations.unarchive.reason.maxLength': { en: 'A reason to bring it back is at most 200 characters.', ar: 'سبب إلغاء الأرشفة 200 حرف على الأكثر.' },
|
|
855
|
+
new: { en: 'New session', ar: 'جلسة جديدة' },
|
|
856
|
+
none: { en: 'No sessions yet.', ar: 'لا توجد جلسات بعد.' },
|
|
857
|
+
dangerZoneWarning: { en: 'These actions change this session for everyone.', ar: 'هذه الإجراءات تغير هذه الجلسة للجميع.' },
|
|
858
|
+
},
|
|
859
|
+
// end add-on: courses
|
|
860
|
+
// add-on: courses
|
|
861
|
+
enrollments: {
|
|
862
|
+
...copyFile(enrollments),
|
|
863
|
+
singular: { en: 'Enrollment', ar: 'تسجيل' },
|
|
864
|
+
plural: { en: 'Enrollments', ar: 'التسجيلات' },
|
|
865
|
+
'operations.others': { en: 'Everyone\'s enrollments', ar: 'تسجيلات الجميع' },
|
|
866
|
+
new: { en: 'New enrollment', ar: 'تسجيل جديد' },
|
|
867
|
+
none: { en: 'No enrollments yet.', ar: 'لا توجد تسجيلات بعد.' },
|
|
868
|
+
dangerZoneWarning: { en: 'These actions change this enrollment for everyone.', ar: 'هذه الإجراءات تغير هذا التسجيل للجميع.' },
|
|
869
|
+
},
|
|
870
|
+
// end add-on: courses
|
|
871
|
+
tags: {
|
|
872
|
+
...copyFile(tags),
|
|
873
|
+
new: { en: 'New tag', ar: 'وسم جديد' },
|
|
874
|
+
none: { en: 'No tags yet.', ar: 'لا توجد وسوم بعد.' },
|
|
875
|
+
dangerZoneWarning: { en: 'These actions change this tag for everyone.', ar: 'هذه الإجراءات تغير هذا الوسم للجميع.' },
|
|
876
|
+
},
|
|
877
|
+
// add-on: media
|
|
878
|
+
media: {
|
|
879
|
+
...copyFile(media),
|
|
880
|
+
new: { en: 'New image', ar: 'صورة جديدة' },
|
|
881
|
+
none: { en: 'No images yet.', ar: 'لا توجد صور بعد.' },
|
|
882
|
+
newAbout: { en: 'Choose an image and describe it. It is uploaded before it is saved.', ar: 'اختر صورة وصِفها. تُرفع قبل أن تُحفظ.' },
|
|
883
|
+
download: { en: 'Download', ar: 'تنزيل' },
|
|
884
|
+
replace: { en: 'Replace file', ar: 'استبدال الملف' },
|
|
885
|
+
replaceAbout: { en: 'Replace file uploads a new image and opens it. This one stays until you delete it.', ar: 'يرفع استبدال الملف صورة جديدة ويفتحها. تبقى هذه الصورة حتى تحذفها.' },
|
|
886
|
+
dangerZoneWarning: { en: 'These actions change this image for everyone.', ar: 'هذه الإجراءات تغير هذه الصورة للجميع.' },
|
|
887
|
+
},
|
|
888
|
+
// end add-on: media
|
|
889
|
+
localUsers: { ...copyFile(localUsers), none: { en: 'No local accounts yet.', ar: 'لا توجد حسابات محلية بعد.' } },
|
|
890
|
+
// add-on: audit-log
|
|
891
|
+
auditLog: copyFile(auditLog),
|
|
892
|
+
backofficeLog: { ...copyFile(backofficeLog), none: { en: 'No staff action yet.', ar: 'لا يوجد إجراء للطاقم بعد.' } },
|
|
893
|
+
// end add-on: audit-log
|
|
894
|
+
// add-on: roles
|
|
895
|
+
memberRoles: {
|
|
896
|
+
...copyFile(memberRoles),
|
|
897
|
+
singular: { en: 'Member role', ar: 'دور عضو' },
|
|
898
|
+
plural: { en: 'Member roles', ar: 'أدوار الأعضاء' },
|
|
899
|
+
none: { en: 'No member roles yet.', ar: 'لا توجد أدوار أعضاء بعد.' },
|
|
900
|
+
},
|
|
901
|
+
// end add-on: roles
|
|
902
|
+
version: {
|
|
903
|
+
saveDraft: { en: 'Save draft', ar: 'حفظ المسودة' },
|
|
904
|
+
noReadableChange: { en: 'No field you may read changed.', ar: 'لم يتغير حقل يمكنك قراءته.' },
|
|
905
|
+
publish: { en: 'Publish', ar: 'نشر' },
|
|
906
|
+
publishChanges: { en: 'Publish changes', ar: 'نشر التغييرات' },
|
|
907
|
+
status: { en: 'Status', ar: 'الحالة' },
|
|
908
|
+
draft: { en: 'Draft', ar: 'مسودة' },
|
|
909
|
+
published: { en: 'Published', ar: 'منشور' },
|
|
910
|
+
changed: { en: 'Changed', ar: 'تغييرات غير منشورة' },
|
|
911
|
+
archived: { en: 'Archived', ar: 'مؤرشف' },
|
|
912
|
+
saving: { en: 'Saving…', ar: 'جارٍ الحفظ…' },
|
|
913
|
+
saved: { en: 'Saved', ar: 'تم الحفظ' },
|
|
914
|
+
updatedAgo: { en: 'Updated {time}', ar: 'تم التحديث {time}' },
|
|
915
|
+
loading: { en: 'Loading versions…', ar: 'جارٍ تحميل الإصدارات…' },
|
|
916
|
+
failed: { en: 'The versions could not be read.', ar: 'تعذرت قراءة الإصدارات.' },
|
|
917
|
+
none: { en: 'No versions yet.', ar: 'لا توجد إصدارات بعد.' },
|
|
918
|
+
updatedAt: { en: 'Updated at', ar: 'تاريخ التحديث' },
|
|
919
|
+
autosave: { en: 'Autosave', ar: 'حفظ تلقائي' },
|
|
920
|
+
currentDraft: { en: 'Current draft', ar: 'المسودة الحالية' },
|
|
921
|
+
currentlyPublished: { en: 'Currently published', ar: 'نشر حاليا' },
|
|
922
|
+
previouslyPublished: { en: 'Previously published', ar: 'نشر سابقا' },
|
|
923
|
+
compareVersions: { en: 'Compare versions', ar: 'قارن الإصدارات' },
|
|
924
|
+
comparingAgainst: { en: 'Comparing against', ar: 'مقارنة مع' },
|
|
925
|
+
currentlyViewing: { en: 'Currently viewing', ar: 'الإصدار المعروض' },
|
|
926
|
+
previousVersion: { en: 'Previous version', ar: 'النسخة السابقة' },
|
|
927
|
+
modifiedOnly: { en: 'Modified only', ar: 'تم التعديل فقط' },
|
|
928
|
+
fieldChanged: { en: 'Changed', ar: 'تغيّر' },
|
|
929
|
+
restoreAsDraft: { en: 'Restore as draft', ar: 'استعادة كمسودة' },
|
|
930
|
+
restoreThisVersion: { en: 'Restore this version', ar: 'استعادة هذا الإصدار' },
|
|
931
|
+
confirmVersionRestoration: { en: 'Confirm version restoration', ar: 'تأكيد استعادة الإصدار' },
|
|
932
|
+
aboutToRestore: {
|
|
933
|
+
en: 'You are about to restore this {label} to the state it was in on {versionDate}.',
|
|
934
|
+
ar: 'أنت على وشك استعادة هذا السجل ({label}) كما كان في {versionDate}.',
|
|
935
|
+
},
|
|
936
|
+
restore: { en: 'Restore', ar: 'استعادة' },
|
|
937
|
+
problemRestoringVersion: { en: 'There was a problem restoring this version', ar: 'حدث خطأ في استعادة هذه النّسخة' },
|
|
938
|
+
more: { en: 'Show older', ar: 'عرض الأقدم' },
|
|
939
|
+
shownCopy: { en: 'Version shown', ar: 'النسخة المعروضة' },
|
|
940
|
+
editingDraft: { en: 'You are editing a draft. The published version is different.', ar: 'أنت تحرر مسودة. النسخة المنشورة مختلفة.' },
|
|
941
|
+
viewPublished: { en: 'View published', ar: 'عرض النسخة المنشورة' },
|
|
942
|
+
readOnlyPublished: { en: 'Read only. This is the published version.', ar: 'للقراءة فقط. هذه هي النسخة المنشورة.' },
|
|
943
|
+
confirmRevertToSaved: { en: 'Confirm revert to saved', ar: 'تأكيد الرّجوع للنسخة المنشورة' },
|
|
944
|
+
aboutToRevertToPublished: {
|
|
945
|
+
en: 'You are about to revert this document\'s changes to its published state. Are you sure?',
|
|
946
|
+
ar: 'أنت على وشك إعادة هذا المستند إلى حالته المنشورة. هل أنت متأكّد؟',
|
|
947
|
+
},
|
|
948
|
+
},
|
|
949
|
+
columns: {
|
|
950
|
+
title: { en: 'Columns', ar: 'الأعمدة' },
|
|
951
|
+
customize: { en: 'Customize Columns', ar: 'تخصيص الأعمدة' },
|
|
952
|
+
shown: { en: 'Shown', ar: 'معروضة' },
|
|
953
|
+
hidden: { en: 'Not shown', ar: 'غير معروضة' },
|
|
954
|
+
up: { en: 'Move up', ar: 'نقل لأعلى' },
|
|
955
|
+
down: { en: 'Move down', ar: 'نقل لأسفل' },
|
|
956
|
+
},
|
|
957
|
+
filter: {
|
|
958
|
+
where: { en: 'Where', ar: 'حيث' },
|
|
959
|
+
join: { en: 'Join', ar: 'الرابط' },
|
|
960
|
+
and: { en: 'and', ar: 'و' },
|
|
961
|
+
or: { en: 'or', ar: 'أو' },
|
|
962
|
+
field: { en: 'Field', ar: 'الحقل' },
|
|
963
|
+
operator: { en: 'Operator', ar: 'العامل' },
|
|
964
|
+
remove: { en: 'Remove', ar: 'إزالة' },
|
|
965
|
+
add: { en: 'Add filter', ar: 'إضافة مرشح' },
|
|
966
|
+
},
|
|
967
|
+
/**
|
|
968
|
+
* One entry per operator the panel offers. `contains` and `like` must read differently
|
|
969
|
+
* in every locale: the store splits `like` on whitespace and runs one `ilike` per word.
|
|
970
|
+
*/
|
|
971
|
+
operator: {
|
|
972
|
+
contains: { en: 'contains', ar: 'يحتوي' },
|
|
973
|
+
equals: { en: 'is', ar: 'يساوي' },
|
|
974
|
+
exists: { en: 'exists', ar: 'موجود' },
|
|
975
|
+
greater_than: { en: 'is after', ar: 'بعد' },
|
|
976
|
+
greater_than_equal: { en: 'is at least', ar: 'على الأقل' },
|
|
977
|
+
in: { en: 'is one of', ar: 'واحد من' },
|
|
978
|
+
less_than: { en: 'is before', ar: 'قبل' },
|
|
979
|
+
less_than_equal: { en: 'is at most', ar: 'على الأكثر' },
|
|
980
|
+
like: { en: 'matches every word', ar: 'يطابق كل كلمة' },
|
|
981
|
+
not_equals: { en: 'is not', ar: 'لا يساوي' },
|
|
982
|
+
not_in: { en: 'is none of', ar: 'ليس واحدا من' },
|
|
983
|
+
},
|
|
984
|
+
error: {
|
|
985
|
+
'invalid.value': { en: 'That value will not do.', ar: 'هذه القيمة غير صالحة.' },
|
|
986
|
+
'invalid.fields_one': { en: 'The following field is invalid: {fields}', ar: 'الحقل التالي غير صالح: {fields}' },
|
|
987
|
+
'invalid.fields_two': { en: 'The following fields are invalid: {fields}', ar: 'الحقلان التاليان غير صالحين: {fields}' },
|
|
988
|
+
'invalid.fields_other': { en: 'The following fields are invalid: {fields}', ar: 'الحقول التالية غير صالحة: {fields}' },
|
|
989
|
+
'forbidden.create': { en: 'You may not write this.', ar: 'لا يمكنك كتابة هذا.' },
|
|
990
|
+
'forbidden.update': { en: 'You may not change this.', ar: 'لا يمكنك تغيير هذا.' },
|
|
991
|
+
'forbidden.delete': { en: 'You may not delete this.', ar: 'لا يمكنك حذف هذا.' },
|
|
992
|
+
'forbidden.read': { en: 'You may not read this.', ar: 'لا يمكنك قراءة هذا.' },
|
|
993
|
+
'forbidden.permission': { en: 'You do not hold the permission this needs.', ar: 'لا تملك الصلاحية التي يتطلبها هذا.' },
|
|
994
|
+
'forbidden.local_auth_off': {
|
|
995
|
+
en: 'Local sign-in is off on this server: LOCAL_AUTH_SECRET is not set. Set it to a secret of 32 characters at least and restart the server.',
|
|
996
|
+
ar: 'تسجيل الدخول المحلي متوقف على هذا الخادم: لم يُضبط LOCAL_AUTH_SECRET. اضبطه بسر من 32 حرفا على الأقل وأعد تشغيل الخادم.',
|
|
997
|
+
},
|
|
998
|
+
'not_found.row': { en: 'That row is no longer there.', ar: 'لم يعد هذا السجل موجودا.' },
|
|
999
|
+
'conflict.retry': { en: 'Another write interrupted this transaction. Try again.', ar: 'تعارضت هذه المعاملة مع عملية أخرى. حاول مرة أخرى.' },
|
|
1000
|
+
'conflict.target': { en: 'The authorized record cannot change.', ar: 'لا يمكن تغيير السجل المصرح به.' },
|
|
1001
|
+
'conflict.unique': { en: 'Another row already holds that value.', ar: 'يحمل سجل آخر هذه القيمة بالفعل.' },
|
|
1002
|
+
'conflict.stale': { en: 'This was changed by someone else since you opened it. Reload it and make your change again.', ar: 'غيّر شخص آخر هذا منذ فتحته. أعد تحميله ثم أعد إجراء تغييرك.' },
|
|
1003
|
+
'invalid.reference': { en: 'That record is not one you may choose.', ar: 'هذا السجل ليس مما يمكنك اختياره.' },
|
|
1004
|
+
'invalid.tenant_reference': { en: 'That record is not one of this organization\'s.', ar: 'هذا السجل ليس من سجلات هذه المؤسسة.' },
|
|
1005
|
+
'forbidden.assign': { en: 'You may not hand over a role that holds a permission you do not hold.', ar: 'لا يمكنك منح دور يحمل صلاحية لا تملكها.' },
|
|
1006
|
+
'invalid.role': { en: 'That role is not one of this organization\'s.', ar: 'هذا الدور ليس من أدوار هذه المؤسسة.' },
|
|
1007
|
+
'forbidden.system_role': { en: 'This role ships with the project and cannot be changed.', ar: 'هذا الدور يأتي مع المشروع ولا يمكن تغييره.' },
|
|
1008
|
+
'invalid.permission_fields': { en: 'A role that holds an operation of a collection must also hold at least one of its fields, or every read of it is refused.', ar: 'الدور الذي يملك عملية على مجموعة يجب أن يملك حقلا واحدا منها على الأقل، وإلا رُفضت كل قراءة لها.' },
|
|
1009
|
+
'conflict.role_held': { en: 'Members hold this role, so it is not deleted or renamed until none does.', ar: 'يشغل أعضاء هذا الدور، لذا لا يُحذف ولا يُعاد تسميته حتى لا يشغله أحد.' },
|
|
1010
|
+
'conflict.role_assigned': { en: 'An assignment of this organization still names that role, so no role takes the name until it is removed.', ar: 'ما زال إسناد في هذه المؤسسة يسمي هذا الدور، لذا لا يأخذ أي دور هذا الاسم حتى يُزال.' },
|
|
1011
|
+
'forbidden.own_role': { en: 'You may not take from yourself the role that lets you manage roles.', ar: 'لا يمكنك سحب الدور الذي يتيح لك إدارة الأدوار من نفسك.' },
|
|
1012
|
+
'forbidden.owner_role': { en: 'The owner of the organization always holds the administrator role, so it is not taken from them.', ar: 'مالك المؤسسة يشغل دور المدير دائما، لذا لا يُسحب منه.' },
|
|
1013
|
+
'invalid.member_role': { en: 'A role is named exactly, with no spaces around it.', ar: 'يُسمّى الدور بدقة، دون مسافات حوله.' },
|
|
1014
|
+
'conflict.last_manager': { en: 'This would leave the organization with nobody who may manage roles. Give another member such a role first.', ar: 'سيترك هذا المؤسسة دون أي عضو يملك إدارة الأدوار. امنح عضوا آخر دورا كهذا أولا.' },
|
|
1015
|
+
'invalid.archived_session': { en: 'This session is archived, or its course is, so it takes no new seat. Pick an open session.', ar: 'هذه الحصة مؤرشفة، أو دورتها مؤرشفة، فلا تقبل مقعدا جديدا. اختر حصة مفتوحة.' },
|
|
1016
|
+
// add-on: courses
|
|
1017
|
+
'invalid.enrollment_closed': { en: 'Students of this course do not enroll themselves. Ask your school to enroll you.', ar: 'لا يسجّل طلاب هذه الدورة أنفسهم. اطلب من مدرستك تسجيلك.' },
|
|
1018
|
+
'invalid.session_started': { en: 'This session has started, so its seats no longer change.', ar: 'بدأت هذه الجلسة، فلم تعد مقاعدها تتغير.' },
|
|
1019
|
+
'conflict.session_full': { en: 'Every seat of this session is taken. Pick another session.', ar: 'كل مقاعد هذه الجلسة محجوزة. اختر جلسة أخرى.' },
|
|
1020
|
+
// end add-on: courses
|
|
1021
|
+
// add-on: courses, settings
|
|
1022
|
+
'invalid.portal_closed': { en: 'The portal is closed for maintenance, so no enrollment changes from it just now.', ar: 'البوابة مغلقة للصيانة، فلا يتغير منها أي تسجيل الآن.' },
|
|
1023
|
+
// end add-on: courses, settings
|
|
1024
|
+
'invalid.draft_sessions': { en: 'A course saved as a draft makes no sessions. Publish the course, then add its sessions.', ar: 'الدورة المحفوظة كمسودة لا تُنشئ جلسات. انشر الدورة أولًا، ثم أضف جلساتها.' },
|
|
1025
|
+
'invalid.upload_request': { en: 'The file has no name, or no size, that can be uploaded.', ar: 'ليس للملف اسم أو حجم يمكن رفعه.' },
|
|
1026
|
+
'invalid.upload_type': { en: 'Files of this type are not taken here.', ar: 'لا تُقبل هنا ملفات من هذا النوع.' },
|
|
1027
|
+
'invalid.upload_size': { en: 'The file is larger than this place takes.', ar: 'الملف أكبر مما يُقبل هنا.' },
|
|
1028
|
+
'invalid.upload_reference': { en: 'The upload has expired or is not this one. Upload the file again.', ar: 'انتهت صلاحية الرفع أو أنه ليس لهذا الملف. ارفع الملف مرة أخرى.' },
|
|
1029
|
+
'invalid.upload_verify': { en: 'The file is not what it said it was, so it was not kept. Upload another.', ar: 'الملف ليس كما ادّعى، لذا لم يُحفظ. ارفع ملفا آخر.' },
|
|
1030
|
+
'not_found.file': { en: 'That file is not here.', ar: 'هذا الملف غير موجود.' },
|
|
1031
|
+
'conflict.referenced': { en: 'Something still uses this, so it is not deleted.', ar: 'ما زال شيء يستخدم هذا، لذا لا يُحذف.' },
|
|
1032
|
+
'conflict.member_holds_roles': {
|
|
1033
|
+
en: 'This member still holds a role in this organization. Take each role from them first, then remove them.',
|
|
1034
|
+
ar: 'ما زال هذا العضو يشغل دورا في هذه المؤسسة. اسحب منه كل دور أولا، ثم أزله.',
|
|
1035
|
+
},
|
|
1036
|
+
'conflict.referencedBy': { en: '{kind} still use this, so it is not deleted.', ar: 'ما زالت {kind} تستخدم هذا، لذا لا يُحذف.' },
|
|
1037
|
+
'conflict.usedBy': { en: 'Used by {count} {kind}: {titles}.', ar: 'تستخدمه {kind} ({count}): {titles}.' },
|
|
1038
|
+
'conflict.usedByUnnamed': { en: 'Used by {count} {kind}.', ar: 'تستخدمه {kind} ({count}).' },
|
|
1039
|
+
'conflict.usedMore': { en: '{count} more', ar: '{count} أخرى' },
|
|
1040
|
+
'conflict.usedHint': { en: 'Take it out of them first.', ar: 'أزله منها أولا.' },
|
|
1041
|
+
'conflict.sole_owner': {
|
|
1042
|
+
en: 'This person is the only owner of an organization, so they are not deleted. Make another member its owner first.',
|
|
1043
|
+
ar: 'هذا الشخص هو المالك الوحيد لمؤسسة، لذا لا يُحذف. اجعل عضوا آخر مالكا لها أولا.',
|
|
1044
|
+
},
|
|
1045
|
+
},
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
type Wording = Record<string, Record<string, { en: string; ar: string }>>
|
|
1049
|
+
|
|
1050
|
+
/** With one organization, nobody founds one on signing up: an owner sets the app up once. */
|
|
1051
|
+
const one: Wording = {
|
|
1052
|
+
auth: {
|
|
1053
|
+
noOrganization: { en: '{email} has no access here yet.', ar: 'لا يملك {email} وصولا هنا بعد.' },
|
|
1054
|
+
},
|
|
1055
|
+
marketing: {
|
|
1056
|
+
howStep1Title: { en: 'Set it up once', ar: 'أعدّه مرة واحدة' },
|
|
1057
|
+
howStep1Description: { en: 'Its owner sets the app up, then invites the people who use it.', ar: 'يُعدّ مالكه التطبيق، ثم يدعو من يستعملونه.' },
|
|
1058
|
+
ctaSubtitle: { en: 'Sign in and add your first record in minutes.', ar: 'سجل الدخول وأضف أول سجل لك خلال دقائق.' },
|
|
1059
|
+
},
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
/** With the one organization never shown, it is the app itself, so no page names an organization. */
|
|
1063
|
+
const unnamed: Wording = {
|
|
1064
|
+
auth: {
|
|
1065
|
+
invitationJoin: { en: 'You are invited to join this app.', ar: 'أنت مدعو للانضمام إلى هذا التطبيق.' },
|
|
1066
|
+
invitationInviterGone: {
|
|
1067
|
+
en: 'Whoever sent this invitation no longer uses this app, so it cannot be accepted. Ask someone else to invite you.',
|
|
1068
|
+
ar: 'من أرسل هذه الدعوة لم يعد يستعمل هذا التطبيق، لذا لا يمكن قبولها. اطلب من شخص آخر أن يدعوك.',
|
|
1069
|
+
},
|
|
1070
|
+
noAccessDescription: { en: '{email} holds no role that opens a screen here. Ask an administrator to give you one.', ar: '{email} لا يملك دورا يفتح أي شاشة هنا. اطلب من أحد المسؤولين أن يمنحك دورا.' },
|
|
1071
|
+
},
|
|
1072
|
+
admin: {
|
|
1073
|
+
noColumns: { en: 'Your role can\'t see any column of this list. Ask an administrator for access.', ar: 'لا يستطيع دورك رؤية أي عمود في هذه القائمة. اطلب الوصول من أحد المسؤولين.' },
|
|
1074
|
+
},
|
|
1075
|
+
marketing: {
|
|
1076
|
+
heroTitle: { en: 'Run your work without the busywork', ar: 'أدر عملك دون العمل الروتيني' },
|
|
1077
|
+
heroSubtitle: {
|
|
1078
|
+
en: 'Asas is a dashboard and a portal: keep your records in one place, and keep every role to what it should see.',
|
|
1079
|
+
ar: 'أساس لوحة تحكم وبوابة: احفظ سجلاتك في مكان واحد، واحصر كل دور بما ينبغي أن يراه.',
|
|
1080
|
+
},
|
|
1081
|
+
featuresTitle: { en: 'Everything you need', ar: 'كل ما تحتاجه' },
|
|
1082
|
+
featureRolesDescription: {
|
|
1083
|
+
en: 'Every screen, field and action follows what a role may read or write.',
|
|
1084
|
+
ar: 'تتبع كل شاشة وحقل وإجراء ما يحق للدور قراءته أو تعديله.',
|
|
1085
|
+
},
|
|
1086
|
+
howStep2Description: { en: 'List what you keep and decide what each role may do.', ar: 'أدرج ما تحفظه وحدد ما يحق لكل دور القيام به.' },
|
|
1087
|
+
ctaTitle: { en: 'Start on Asas', ar: 'ابدأ على أساس' },
|
|
1088
|
+
footer: { en: 'Asas — one place for your records.', ar: 'أساس — مكان واحد لسجلاتك.' },
|
|
1089
|
+
},
|
|
1090
|
+
dashboard: {
|
|
1091
|
+
members: { en: 'Users', ar: 'المستخدمون' },
|
|
1092
|
+
membersAbout: { en: 'Who uses this app, and the role each holds.', ar: 'من يستعمل هذا التطبيق، والدور الذي يشغله كل منهم.' },
|
|
1093
|
+
membersNone: { en: 'No one uses this app yet.', ar: 'لا يستعمل أحد هذا التطبيق بعد.' },
|
|
1094
|
+
inviteAbout: { en: 'They join as a user once they accept. Give them roles here after that.', ar: 'ينضم مستخدمًا بعد أن يقبل الدعوة. امنحه الأدوار هنا بعد ذلك.' },
|
|
1095
|
+
inviteAlreadyMember: { en: 'This person already uses this app.', ar: 'هذا الشخص يستعمل هذا التطبيق بالفعل.' },
|
|
1096
|
+
inviteNotAllowed: { en: 'You may not invite people.', ar: 'لا يحق لك دعوة أشخاص.' },
|
|
1097
|
+
removeMember: { en: 'Remove from the app', ar: 'إزالة من التطبيق' },
|
|
1098
|
+
removeAbout: {
|
|
1099
|
+
en: 'They lose access to this app and every role they hold in it. Inviting them again does not give the roles back.',
|
|
1100
|
+
ar: 'يفقد الوصول إلى هذا التطبيق وكل الأدوار التي يشغلها فيه. دعوته مرة أخرى لا تعيد إليه أدواره.',
|
|
1101
|
+
},
|
|
1102
|
+
removeLastOwner: { en: 'The app\'s only owner cannot be removed.', ar: 'لا يمكن إزالة المالك الوحيد للتطبيق.' },
|
|
1103
|
+
removeNotAllowed: { en: 'You may not remove users.', ar: 'لا يحق لك إزالة مستخدمين.' },
|
|
1104
|
+
rolesAbout: { en: 'Fixed roles ship with the project and cannot be changed here. Custom roles are this app\'s own.', ar: 'الأدوار الثابتة تأتي مع المشروع ولا يمكن تغييرها هنا. الأدوار المخصصة خاصة بهذا التطبيق.' },
|
|
1105
|
+
membersOwnerRole: { en: 'The owner of the app always holds this role.', ar: 'مالك التطبيق يشغل هذا الدور دائما.' },
|
|
1106
|
+
dashboardAbout: { en: 'What this app holds, and how many have enrolled lately.', ar: 'ما يضمه هذا التطبيق، وعدد من سجلوا مؤخرا.' },
|
|
1107
|
+
dashboardAboutCatalogue: { en: 'What this app holds.', ar: 'ما يضمه هذا التطبيق.' },
|
|
1108
|
+
},
|
|
1109
|
+
error: {
|
|
1110
|
+
'invalid.tenant_reference': { en: 'That record is not one of this app\'s.', ar: 'هذا السجل ليس من سجلات هذا التطبيق.' },
|
|
1111
|
+
'conflict.last_manager': { en: 'This would leave nobody who may manage roles. Give another user such a role first.', ar: 'سيترك هذا التطبيق دون أي مستخدم يملك إدارة الأدوار. امنح مستخدما آخر دورا كهذا أولا.' },
|
|
1112
|
+
'forbidden.owner_role': { en: 'The owner of the app always holds the administrator role, so it is not taken from them.', ar: 'مالك التطبيق يشغل دور المدير دائما، لذا لا يُسحب منه.' },
|
|
1113
|
+
'invalid.role': { en: 'That role is not one of this app\'s.', ar: 'هذا الدور ليس من أدوار هذا التطبيق.' },
|
|
1114
|
+
'conflict.role_assigned': { en: 'An assignment still names that role, so no role takes the name until it is removed.', ar: 'ما زال إسناد يسمي هذا الدور، لذا لا يأخذ أي دور هذا الاسم حتى يُزال.' },
|
|
1115
|
+
'conflict.member_holds_roles': { en: 'This user still holds a role. Take each role from them first, then remove them.', ar: 'ما زال هذا المستخدم يشغل دورا. اسحب منه كل دور أولا، ثم أزله.' },
|
|
1116
|
+
'conflict.sole_owner': { en: 'This person is the app\'s only owner, so they are not deleted. Make another user an owner first.', ar: 'هذا الشخص هو المالك الوحيد للتطبيق، لذا لا يُحذف. اجعل مستخدما آخر مالكا أولا.' },
|
|
1117
|
+
},
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
/** The words the organization mode changes, laid over the base copy key by key. */
|
|
1121
|
+
export const wordingFor = (mode: { organizations: 'one' | 'many'; showOrganization: boolean }): Wording =>
|
|
1122
|
+
mode.organizations === 'many' ? {} : mode.showOrganization ? one : Object.fromEntries([...new Set([...Object.keys(one), ...Object.keys(unnamed)])].map((scope) => [scope, { ...one[scope], ...unnamed[scope] }]))
|
|
1123
|
+
|
|
1124
|
+
/** The base copy with `wording` laid over it, so a key the mode changes reads the mode's words. */
|
|
1125
|
+
export const withWording = (base: typeof ownScopes, wording: Wording) =>
|
|
1126
|
+
Object.fromEntries(Object.entries(base).map(([scope, keys]) => [scope, { ...keys, ...wording[scope] }])) as typeof ownScopes
|
|
1127
|
+
|
|
1128
|
+
/**
|
|
1129
|
+
* Every scope this app's `t` can answer from, in this project's organization mode. Each entry
|
|
1130
|
+
* carries a string per declared locale: a key left unanswered here shows an Arabic viewer the
|
|
1131
|
+
* English fallback.
|
|
1132
|
+
*/
|
|
1133
|
+
export const scopes = withWording(ownScopes, wordingFor({ organizations, showOrganization }))
|
|
1134
|
+
|
|
1135
|
+
// A `Map`, so a code named for a key of `Object.prototype` answers nothing. A taken organization
|
|
1136
|
+
// name has no entry: names may repeat, and sign-up makes the slug unique itself.
|
|
1137
|
+
const refusalKeys = new Map([
|
|
1138
|
+
['INVALID_EMAIL_OR_PASSWORD', 'auth.invalidCredentials'],
|
|
1139
|
+
['EMAIL_NOT_VERIFIED', 'auth.emailNotVerified'],
|
|
1140
|
+
['BANNED_USER', 'auth.banned'],
|
|
1141
|
+
['USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL', 'auth.userExists'],
|
|
1142
|
+
['INVALID_EMAIL', 'auth.invalidEmail'],
|
|
1143
|
+
['INVITATION_NOT_FOUND', 'auth.invitationGone'],
|
|
1144
|
+
['YOU_ARE_NOT_THE_RECIPIENT_OF_THE_INVITATION', 'auth.invitationNotYours'],
|
|
1145
|
+
['INVITER_IS_NO_LONGER_A_MEMBER_OF_THE_ORGANIZATION', 'auth.invitationInviterGone'],
|
|
1146
|
+
['USER_IS_ALREADY_A_MEMBER_OF_THIS_ORGANIZATION', 'dashboard.inviteAlreadyMember'],
|
|
1147
|
+
['USER_IS_ALREADY_INVITED_TO_THIS_ORGANIZATION', 'dashboard.inviteAlreadyInvited'],
|
|
1148
|
+
['YOU_ARE_NOT_ALLOWED_TO_INVITE_USERS_TO_THIS_ORGANIZATION', 'dashboard.inviteNotAllowed'],
|
|
1149
|
+
['YOU_ARE_NOT_ALLOWED_TO_CANCEL_THIS_INVITATION', 'dashboard.inviteCancelNotAllowed'],
|
|
1150
|
+
['YOU_CANNOT_LEAVE_THE_ORGANIZATION_AS_THE_ONLY_OWNER', 'dashboard.removeLastOwner'],
|
|
1151
|
+
['YOU_ARE_NOT_ALLOWED_TO_DELETE_THIS_MEMBER', 'dashboard.removeNotAllowed'],
|
|
1152
|
+
['RESET_PASSWORD_DISABLED', 'auth.resetDisabled'],
|
|
1153
|
+
['INVALID_TOKEN', 'auth.tokenInvalid'],
|
|
1154
|
+
['SETUP_CODE_WRONG', 'auth.setupCodeWrong'],
|
|
1155
|
+
['SETUP_CODE_UNSET', 'auth.setupCodeUnset'],
|
|
1156
|
+
['FIRST_REGISTER_CLOSED', 'auth.firstRegisterClosed'],
|
|
1157
|
+
['FIRST_RUN_CLOSED', 'auth.firstRunClosed'],
|
|
1158
|
+
['FIRST_RUN_PENDING', 'auth.setupPending'],
|
|
1159
|
+
['SIGN_UP_INVITE_ONLY', 'auth.inviteOnly'],
|
|
1160
|
+
['SIGN_UP_DOMAIN', 'auth.outsideDomains'],
|
|
1161
|
+
])
|
|
1162
|
+
/** The key of this app's own sentence for a provider refusal, or nothing. The provider's `message` is English on every page and is never shown. */
|
|
1163
|
+
export const refusalKey = (status: number, code: unknown): string | undefined =>
|
|
1164
|
+
status === 429 ? 'auth.tooManyRequests' : typeof code === 'string' ? refusalKeys.get(code) : undefined
|
|
1165
|
+
|
|
1166
|
+
/** The `t` a screen is handed, for one locale. A key splits at its first dot into scope and key; an unanswered key returns the caller's fallback. */
|
|
1167
|
+
export const translator =
|
|
1168
|
+
(which: string): Translate =>
|
|
1169
|
+
(key, fallback) => {
|
|
1170
|
+
const dot = key.indexOf('.')
|
|
1171
|
+
if (dot < 0) return fallback
|
|
1172
|
+
return scopes[key.slice(0, dot)]?.[key.slice(dot + 1)]?.[which] ?? fallback
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
/**
|
|
1176
|
+
* A route's `head`: the tab names the page, then the app, or the app alone where the page
|
|
1177
|
+
* names nothing. `app` is a key the root route puts in its context beside the locale.
|
|
1178
|
+
*/
|
|
1179
|
+
export const documentHead = ({ locale, app }: { locale: Locale; app?: string }, page?: (t: Translate) => string | undefined) => {
|
|
1180
|
+
const t = translator(locale)
|
|
1181
|
+
const own = app ? t(app, app)! : ''
|
|
1182
|
+
const name = page?.(t)
|
|
1183
|
+
return { meta: [{ title: name && own ? `${name} · ${own}` : name || own }] }
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
/**
|
|
1187
|
+
* Sets the cookie the request middleware reads and redraws the page in `to` where it stands, as Payload's language
|
|
1188
|
+
* switch refreshes its router: nothing typed into a form is lost. The root route reads the locale off the document.
|
|
1189
|
+
*/
|
|
1190
|
+
export const showLocale = async ({ to, router }: { to: string; router: { invalidate(): Promise<unknown> } }) => {
|
|
1191
|
+
const locale = localeOf(to)
|
|
1192
|
+
document.cookie = localeCookieValue(locale)
|
|
1193
|
+
document.documentElement.lang = locale
|
|
1194
|
+
document.documentElement.dir = dirOf(locale)
|
|
1195
|
+
await router.invalidate()
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
/**
|
|
1199
|
+
* Sets the cookie the request middleware reads, then reloads so the server draws the document
|
|
1200
|
+
* in it: a screen's form is seeded again from whatever row the host hands, so the shell asks before it
|
|
1201
|
+
* drops a change nobody saved, and its own unsaved-changes guard is dropped because the shell already asked.
|
|
1202
|
+
*/
|
|
1203
|
+
export const switchLocale = ({ to }: { to: string }): void => {
|
|
1204
|
+
window.onbeforeunload = null
|
|
1205
|
+
document.cookie = localeCookieValue(localeOf(to))
|
|
1206
|
+
window.location.reload()
|
|
1207
|
+
}
|