Add Hebrew translation and RTL support via Docker injection

Injects custom CSS and JS into the Paperclip UI during Docker build:
- RTL layout overrides for Tailwind, Radix UI, and MDXEditor
- Hebrew translation script with MutationObserver for dynamic content
- Patch script to modify index.html with dir="rtl" and lang="he"

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Chaim
2026-04-04 20:41:06 +00:00
parent de82ce18f4
commit 5160741248
4 changed files with 1666 additions and 1 deletions

View File

@@ -4,6 +4,12 @@ RUN apt-get update && apt-get install -y --no-install-recommends curl && rm -rf
RUN npm install -g paperclipai RUN npm install -g paperclipai
# Inject Hebrew RTL + translation into the UI
COPY assets/rtl-override.css /tmp/rtl-override.css
COPY assets/translate-he.js /tmp/translate-he.js
COPY patch-html.sh /tmp/patch-html.sh
RUN bash /tmp/patch-html.sh && rm -f /tmp/patch-html.sh /tmp/rtl-override.css /tmp/translate-he.js
RUN mkdir -p /data/instances/default/data/storage \ RUN mkdir -p /data/instances/default/data/storage \
/data/instances/default/data/backups \ /data/instances/default/data/backups \
/data/instances/default/secrets \ /data/instances/default/secrets \

741
assets/rtl-override.css Normal file
View File

@@ -0,0 +1,741 @@
/* ==========================================================================
Paperclip AI - RTL Override for Hebrew (dir="rtl")
Generated from analysis of index-CYurTMty.css (Tailwind v4.1.18 + MDXEditor)
Scoped under html[dir="rtl"] to apply only in RTL mode.
========================================================================== */
/* --------------------------------------------------------------------------
1. GLOBAL DIRECTION
-------------------------------------------------------------------------- */
html[dir="rtl"] {
direction: rtl;
}
html[dir="rtl"] body {
text-align: right;
}
/* --------------------------------------------------------------------------
2. TAILWIND UTILITY CLASSES - Padding Left/Right Swap
.pl-* -> padding-right, .pr-* -> padding-left
-------------------------------------------------------------------------- */
html[dir="rtl"] .pl-1 { padding-left: 0; padding-right: calc(var(--spacing) * 1); }
html[dir="rtl"] .pl-2 { padding-left: 0; padding-right: calc(var(--spacing) * 2); }
html[dir="rtl"] .pl-3 { padding-left: 0; padding-right: calc(var(--spacing) * 3); }
html[dir="rtl"] .pl-4 { padding-left: 0; padding-right: calc(var(--spacing) * 4); }
html[dir="rtl"] .pl-5 { padding-left: 0; padding-right: calc(var(--spacing) * 5); }
html[dir="rtl"] .pl-5\.5 { padding-left: 0; padding-right: calc(var(--spacing) * 5.5); }
html[dir="rtl"] .pl-6 { padding-left: 0; padding-right: calc(var(--spacing) * 6); }
html[dir="rtl"] .pl-7 { padding-left: 0; padding-right: calc(var(--spacing) * 7); }
html[dir="rtl"] .pl-8 { padding-left: 0; padding-right: calc(var(--spacing) * 8); }
html[dir="rtl"] .pr-1 { padding-right: 0; padding-left: calc(var(--spacing) * 1); }
html[dir="rtl"] .pr-2 { padding-right: 0; padding-left: calc(var(--spacing) * 2); }
html[dir="rtl"] .pr-3 { padding-right: 0; padding-left: calc(var(--spacing) * 3); }
html[dir="rtl"] .pr-6 { padding-right: 0; padding-left: calc(var(--spacing) * 6); }
html[dir="rtl"] .pr-8 { padding-right: 0; padding-left: calc(var(--spacing) * 8); }
html[dir="rtl"] .pr-10 { padding-right: 0; padding-left: calc(var(--spacing) * 10); }
/* data-inset padding override */
html[dir="rtl"] .data-\[inset\]\:pl-8[data-inset] {
padding-left: 0;
padding-right: calc(var(--spacing) * 8);
}
/* Responsive padding overrides */
html[dir="rtl"] .sm\:pl-1 { padding-left: 0; padding-right: calc(var(--spacing) * 1); }
html[dir="rtl"] .sm\:pr-3 { padding-right: 0; padding-left: calc(var(--spacing) * 3); }
/* --------------------------------------------------------------------------
3. TAILWIND UTILITY CLASSES - Margin Left/Right Swap
.ml-* -> margin-right, .mr-* -> margin-left
-------------------------------------------------------------------------- */
html[dir="rtl"] .-ml-1\.5 { margin-left: 0; margin-right: calc(var(--spacing) * -1.5); }
html[dir="rtl"] .ml-0\.5 { margin-left: 0; margin-right: calc(var(--spacing) * 0.5); }
html[dir="rtl"] .ml-1 { margin-left: 0; margin-right: calc(var(--spacing) * 1); }
html[dir="rtl"] .ml-1\.5 { margin-left: 0; margin-right: calc(var(--spacing) * 1.5); }
html[dir="rtl"] .ml-2 { margin-left: 0; margin-right: calc(var(--spacing) * 2); }
html[dir="rtl"] .ml-3 { margin-left: 0; margin-right: calc(var(--spacing) * 3); }
html[dir="rtl"] .ml-4 { margin-left: 0; margin-right: calc(var(--spacing) * 4); }
html[dir="rtl"] .ml-5 { margin-left: 0; margin-right: calc(var(--spacing) * 5); }
html[dir="rtl"] .ml-auto { margin-left: 0; margin-right: auto; }
html[dir="rtl"] .mr-1 { margin-right: 0; margin-left: calc(var(--spacing) * 1); }
html[dir="rtl"] .mr-1\.5 { margin-right: 0; margin-left: calc(var(--spacing) * 1.5); }
html[dir="rtl"] .mr-2 { margin-right: 0; margin-left: calc(var(--spacing) * 2); }
html[dir="rtl"] .mr-auto { margin-right: 0; margin-left: auto; }
/* file selector button margin */
html[dir="rtl"] .file\:mr-4::file-selector-button {
margin-right: 0;
margin-left: calc(var(--spacing) * 4);
}
/* Responsive margin overrides */
html[dir="rtl"] .sm\:mr-1 { margin-right: 0; margin-left: calc(var(--spacing) * 1); }
html[dir="rtl"] .md\:ml-auto { margin-left: 0; margin-right: auto; }
/* --------------------------------------------------------------------------
4. TAILWIND UTILITY CLASSES - Left/Right Position Swap
-------------------------------------------------------------------------- */
html[dir="rtl"] .left-0 { left: auto; right: calc(var(--spacing) * 0); }
html[dir="rtl"] .left-1\/2 { left: auto; right: 50%; }
html[dir="rtl"] .left-2 { left: auto; right: calc(var(--spacing) * 2); }
html[dir="rtl"] .left-2\.5 { left: auto; right: calc(var(--spacing) * 2.5); }
html[dir="rtl"] .left-3 { left: auto; right: calc(var(--spacing) * 3); }
html[dir="rtl"] .left-4 { left: auto; right: calc(var(--spacing) * 4); }
html[dir="rtl"] .left-\[-14px\] { left: auto; right: -14px; }
html[dir="rtl"] .left-\[50\%\] { left: auto; right: 50%; }
html[dir="rtl"] .-right-0\.5 { right: auto; left: calc(var(--spacing) * -0.5); }
html[dir="rtl"] .-right-2 { right: auto; left: calc(var(--spacing) * -2); }
html[dir="rtl"] .right-0 { right: auto; left: calc(var(--spacing) * 0); }
html[dir="rtl"] .right-1\.5 { right: auto; left: calc(var(--spacing) * 1.5); }
html[dir="rtl"] .right-2 { right: auto; left: calc(var(--spacing) * 2); }
html[dir="rtl"] .right-3 { right: auto; left: calc(var(--spacing) * 3); }
html[dir="rtl"] .right-4 { right: auto; left: calc(var(--spacing) * 4); }
html[dir="rtl"] .right-6 { right: auto; left: calc(var(--spacing) * 6); }
/* Responsive left positioning */
html[dir="rtl"] .sm\:left-3 { left: auto; right: calc(var(--spacing) * 3); }
/* --------------------------------------------------------------------------
5. TAILWIND UTILITY CLASSES - Text Align Swap
-------------------------------------------------------------------------- */
html[dir="rtl"] .text-left { text-align: right; }
html[dir="rtl"] .text-right { text-align: left; }
html[dir="rtl"] .sm\:text-left { text-align: right; }
html[dir="rtl"] .sm\:text-right { text-align: left; }
/* --------------------------------------------------------------------------
6. TAILWIND UTILITY CLASSES - Float Swap
-------------------------------------------------------------------------- */
html[dir="rtl"] .float-right { float: left; }
/* --------------------------------------------------------------------------
7. TAILWIND UTILITY CLASSES - Border Left/Right Swap
-------------------------------------------------------------------------- */
html[dir="rtl"] .border-l {
border-left-style: none;
border-left-width: 0;
border-right-style: var(--tw-border-style);
border-right-width: 1px;
}
html[dir="rtl"] .border-l-2 {
border-left-style: none;
border-left-width: 0;
border-right-style: var(--tw-border-style);
border-right-width: 2px;
}
html[dir="rtl"] .border-l-transparent {
border-left-color: initial;
border-right-color: #0000;
}
html[dir="rtl"] .border-r {
border-right-style: none;
border-right-width: 0;
border-left-style: var(--tw-border-style);
border-left-width: 1px;
}
/* Responsive border overrides */
html[dir="rtl"] .sm\:border-l {
border-left-style: none;
border-left-width: 0;
border-right-style: var(--tw-border-style);
border-right-width: 1px;
}
html[dir="rtl"] .lg\:border-r {
border-right-style: none;
border-right-width: 0;
border-left-style: var(--tw-border-style);
border-left-width: 1px;
}
/* --------------------------------------------------------------------------
8. TAILWIND UTILITY CLASSES - Rounded Left/Right Swap
-------------------------------------------------------------------------- */
html[dir="rtl"] .rounded-l-md {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
border-top-right-radius: .5rem;
border-bottom-right-radius: .5rem;
}
html[dir="rtl"] .rounded-r-md {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
border-top-left-radius: .5rem;
border-bottom-left-radius: .5rem;
}
html[dir="rtl"] .rounded-r-full {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
border-top-left-radius: 3.40282e38px;
border-bottom-left-radius: 3.40282e38px;
}
/* --------------------------------------------------------------------------
9. TAILWIND UTILITY CLASSES - TranslateX Flip
-------------------------------------------------------------------------- */
html[dir="rtl"] .-translate-x-1\/2 { --tw-translate-x: 50%; }
html[dir="rtl"] .-translate-x-4 { --tw-translate-x: calc(var(--spacing) * 4); }
html[dir="rtl"] .-translate-x-full { --tw-translate-x: 100%; }
html[dir="rtl"] .translate-x-0 { --tw-translate-x: calc(var(--spacing) * 0); }
html[dir="rtl"] .translate-x-0\.5 { --tw-translate-x: calc(var(--spacing) * -0.5); }
html[dir="rtl"] .translate-x-4\.5 { --tw-translate-x: calc(var(--spacing) * -4.5); }
html[dir="rtl"] .translate-x-5 { --tw-translate-x: calc(var(--spacing) * -5); }
html[dir="rtl"] .translate-x-\[-50\%\] { --tw-translate-x: 50%; }
/* --------------------------------------------------------------------------
10. RADIX UI - data-side tooltip/popover offset swap
-------------------------------------------------------------------------- */
html[dir="rtl"] .data-\[side\=left\]\:-translate-x-1[data-side=left] {
--tw-translate-x: calc(var(--spacing) * 1);
}
html[dir="rtl"] .data-\[side\=right\]\:translate-x-1[data-side=right] {
--tw-translate-x: calc(var(--spacing) * -1);
}
/* --------------------------------------------------------------------------
11. MDXEditor - Toolbar Components
-------------------------------------------------------------------------- */
/* Toolbar root - sticky, scrollable row */
html[dir="rtl"] [class*="_toolbarRoot_"] {
flex-direction: row-reverse;
}
/* Toolbar mode switch - push to left in RTL (was margin-left:auto) */
html[dir="rtl"] [class*="_toolbarModeSwitch_"] {
margin-left: 0;
margin-right: auto;
}
/* Toolbar separator - swap border sides */
html[dir="rtl"] [class*="_toolbarRoot_"] div[role="separator"] {
border-left: 1px solid var(--baseBase);
border-right: 1px solid var(--baseBorder);
}
/* Toolbar button adjacency spacing */
html[dir="rtl"] [class*="_toolbarButton_"] + [class*="_toolbarButton_"] {
margin-left: 0;
margin-right: var(--spacing-1);
}
/* Toolbar toggle first/last child border-radius swap */
html[dir="rtl"] [class*="_toolbarToggleSingleGroup_"]:first-of-type [class*="_toolbarToggleItem_"]:only-child,
html[dir="rtl"] [class*="_toolbarToggleSingleGroup_"]:only-child [class*="_toolbarToggleItem_"]:first-child,
html[dir="rtl"] [class*="_toolbarModeSwitch_"] [class*="_toolbarToggleItem_"]:first-child {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
border-top-right-radius: var(--radius-base);
border-bottom-right-radius: var(--radius-base);
}
html[dir="rtl"] [class*="_toolbarToggleSingleGroup_"]:last-of-type [class*="_toolbarToggleItem_"]:only-child,
html[dir="rtl"] [class*="_toolbarToggleSingleGroup_"]:only-child [class*="_toolbarToggleItem_"]:last-child,
html[dir="rtl"] [class*="_toolbarModeSwitch_"] [class*="_toolbarToggleItem_"]:last-child {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
border-top-left-radius: var(--radius-base);
border-bottom-left-radius: var(--radius-base);
}
/* Dropdown arrow - push to right in RTL (was margin-left:auto) */
html[dir="rtl"] [class*="_toolbarNodeKindSelectDropdownArrow_"],
html[dir="rtl"] [class*="_selectDropdownArrow_"] {
margin-left: 0;
margin-right: auto;
}
/* Dropdown container - border radius swap */
html[dir="rtl"] [class*="_toolbarNodeKindSelectContainer_"],
html[dir="rtl"] [class*="_toolbarButtonDropdownContainer_"],
html[dir="rtl"] [class*="_toolbarCodeBlockLanguageSelectContent_"],
html[dir="rtl"] [class*="_selectContainer_"] {
border-bottom-left-radius: 0;
border-bottom-right-radius: var(--radius-base);
}
html[dir="rtl"] [class*="_toolbarButtonDropdownContainer_"],
html[dir="rtl"] [class*="_toolbarButtonDropdownContainer_"] [class*="_selectItem_"]:first-child {
border-top-right-radius: 0;
border-top-left-radius: var(--radius-base);
}
/* Select trigger open state - radius swap */
html[dir="rtl"] [class*="_toolbarNodeKindSelectTrigger_"][data-state="open"],
html[dir="rtl"] [class*="_toolbarButtonSelectTrigger_"][data-state="open"],
html[dir="rtl"] [class*="_selectTrigger_"][data-state="open"] {
border-bottom-right-radius: var(--radius-none);
border-bottom-left-radius: var(--radius-none);
}
/* Select item last child - radius swap */
html[dir="rtl"] [class*="_toolbarNodeKindSelectItem_"]:last-child,
html[dir="rtl"] [class*="_selectItem_"]:last-child {
border-bottom-left-radius: 0;
border-bottom-right-radius: var(--radius-base);
}
/* --------------------------------------------------------------------------
12. MDXEditor - CodeMirror & Image Edit Toolbars
Position: was right:0 -> left:0, border-bottom-left -> border-bottom-right
-------------------------------------------------------------------------- */
html[dir="rtl"] [class*="_codeMirrorToolbar_"] {
right: auto;
left: 0;
border-bottom-left-radius: 0;
border-bottom-right-radius: var(--radius-base);
}
html[dir="rtl"] [class*="_editImageToolbar_"] {
right: auto;
left: 0;
border-bottom-left-radius: 0;
border-bottom-right-radius: var(--radius-base);
}
/* --------------------------------------------------------------------------
13. MDXEditor - Property Panel
-------------------------------------------------------------------------- */
html[dir="rtl"] [class*="_propertyPanelTitle_"] {
padding-left: 0;
padding-right: var(--spacing-2);
}
html[dir="rtl"] [class*="_propertyEditorTable_"] th {
text-align: right;
}
html[dir="rtl"] [class*="_propertyEditorTable_"] [class*="_readOnlyColumnCell_"] {
padding-left: initial;
padding-right: 0;
}
html[dir="rtl"] [class*="_readOnlyColumnCell_"] {
padding-left: initial;
padding-right: 0;
}
html[dir="rtl"] [class*="_propertyEditorTable_"] td:last-child [class*="_iconButton_"] {
margin-left: 0;
margin-right: var(--spacing-4);
}
/* --------------------------------------------------------------------------
14. MDXEditor - Dialog
-------------------------------------------------------------------------- */
/* Dialog centered positioning - no swap needed (translate-50% centers) */
html[dir="rtl"] [class*="_dialogTitle_"] {
padding-left: 0;
padding-right: var(--spacing-2);
}
/* Link dialog input button */
html[dir="rtl"] [class*="_linkDialogInputWrapper_"] > button {
padding-right: 0;
padding-left: var(--spacing-2);
}
/* Link dialog input dropdown open state */
html[dir="rtl"] [class*="_linkDialogInputWrapper_"][data-visible-dropdown="true"] {
border-bottom-left-radius: var(--radius-none);
border-bottom-right-radius: var(--radius-none);
}
/* Link dialog preview anchor */
html[dir="rtl"] [class*="_linkDialogPreviewAnchor_"] {
margin-right: 0;
margin-left: var(--spacing-1);
}
/* --------------------------------------------------------------------------
15. MDXEditor - Generic Component Name
-------------------------------------------------------------------------- */
html[dir="rtl"] [class*="_genericComponentName_"] {
padding-right: 0;
padding-left: var(--spacing-2);
}
/* --------------------------------------------------------------------------
16. MDXEditor - Table Editor
-------------------------------------------------------------------------- */
html[dir="rtl"] [class*="_tableEditor_"] thead > tr > th {
text-align: left; /* was right, swap */
}
html[dir="rtl"] [class*="_toolCell_"] {
text-align: left; /* was right, swap */
}
html[dir="rtl"] [class*="_leftAlignedCell_"] {
text-align: right; /* was left, swap */
}
html[dir="rtl"] [class*="_rightAlignedCell_"] {
text-align: left; /* was right, swap */
}
/* Table column editor toolbar separators */
html[dir="rtl"] [class*="_tableColumnEditorToolbar_"] [role="separator"] {
margin-left: 0;
margin-right: var(--spacing-1);
}
/* Add column button - was on right side */
html[dir="rtl"] [class*="_addColumnButton_"] {
margin-left: 0;
margin-right: var(--spacing-px);
border-top-right-radius: 0;
border-bottom-right-radius: 0;
border-top-left-radius: var(--radius-medium);
border-bottom-left-radius: var(--radius-medium);
}
/* Toggle group first/last child radius swap */
html[dir="rtl"] [class*="_toggleGroupRoot_"] button:first-child {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
border-top-right-radius: var(--radius-base);
border-bottom-right-radius: var(--radius-base);
}
html[dir="rtl"] [class*="_toggleGroupRoot_"] button:last-child {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
border-top-left-radius: var(--radius-base);
border-bottom-left-radius: var(--radius-base);
}
/* --------------------------------------------------------------------------
17. MDXEditor - Diff Source Toggle
-------------------------------------------------------------------------- */
html[dir="rtl"] [class*="_diffSourceToggleWrapper_"] {
margin-left: 0;
margin-right: auto;
right: auto;
left: 0;
}
/* --------------------------------------------------------------------------
18. MDXEditor - Select with Label
-------------------------------------------------------------------------- */
html[dir="rtl"] [class*="_selectWithLabel_"] {
margin-left: 0;
margin-right: var(--spacing-2);
}
html[dir="rtl"] [class*="_toolbarTitleMode_"] {
margin-left: 0;
margin-right: var(--spacing-2);
}
/* --------------------------------------------------------------------------
19. MDXEditor - Downshift Autocomplete
-------------------------------------------------------------------------- */
html[dir="rtl"] [class*="_downshiftInputWrapper_"] > button {
padding-right: 0;
padding-left: var(--spacing-2);
}
html[dir="rtl"] [class*="_downshiftInputWrapper_"][data-visible-dropdown="true"] {
border-bottom-left-radius: var(--radius-none);
border-bottom-right-radius: var(--radius-none);
}
html[dir="rtl"] [class*="_downshiftAutocompleteContainer_"] ul {
border-bottom-left-radius: 0;
border-bottom-right-radius: var(--radius-medium);
}
html[dir="rtl"] [class*="_downshiftAutocompleteContainer_"] ul li:last-of-type {
border-bottom-left-radius: 0;
border-bottom-right-radius: var(--radius-medium);
}
/* --------------------------------------------------------------------------
20. MDXEditor - List Items (Checkbox)
-------------------------------------------------------------------------- */
html[dir="rtl"] [class*="_listItemChecked_"],
html[dir="rtl"] [class*="_listItemUnchecked_"] {
padding-left: 0;
padding-right: var(--spacing-6);
}
html[dir="rtl"] [class*="_listItemChecked_"]:before,
html[dir="rtl"] [class*="_listItemUnchecked_"]:before {
left: auto;
right: 0;
}
html[dir="rtl"] [class*="_listItemChecked_"]:after {
left: auto;
right: var(--spacing-1_5);
}
/* --------------------------------------------------------------------------
21. MDXEditor - Admonitions (Info/Warning/Danger blocks)
-------------------------------------------------------------------------- */
html[dir="rtl"] [class*="_admonitionDanger_"],
html[dir="rtl"] [class*="_admonitionInfo_"],
html[dir="rtl"] [class*="_admonitionNote_"],
html[dir="rtl"] [class*="_admonitionTip_"],
html[dir="rtl"] [class*="_admonitionCaution_"] {
border-left: none;
border-right: 3px solid var(--admonitionBorder);
}
/* --------------------------------------------------------------------------
22. Paperclip Markdown Rendered Content
-------------------------------------------------------------------------- */
html[dir="rtl"] .paperclip-mdxeditor-content ul,
html[dir="rtl"] .paperclip-mdxeditor-content ol {
padding-left: 0;
padding-right: 1.6em;
}
html[dir="rtl"] .paperclip-mdxeditor-content blockquote {
border-left: none;
border-right: 3px solid var(--border);
padding-left: 0;
padding-right: 1em;
}
html[dir="rtl"] .paperclip-markdown :where(ul, ol) {
padding-left: 0;
padding-right: 1.15rem;
}
html[dir="rtl"] .paperclip-markdown li {
padding-left: 0;
padding-right: .2rem;
}
html[dir="rtl"] .paperclip-markdown blockquote {
border-left: none;
border-right: .24rem solid var(--border);
margin-left: initial;
margin-right: 0;
padding-left: 0;
padding-right: .95rem;
}
@supports (color: color-mix(in lab, red, red)) {
html[dir="rtl"] .paperclip-markdown blockquote {
border-right: .24rem solid color-mix(in oklab, var(--border) 84%, var(--muted-foreground) 16%);
}
}
html[dir="rtl"] .paperclip-markdown th {
text-align: right;
}
/* Nested list/ol padding overrides from Tailwind arbitrary selectors */
html[dir="rtl"] .\[\&_ol\]\:pl-5 ol {
padding-left: 0;
padding-right: calc(var(--spacing) * 5);
}
html[dir="rtl"] .\[\&_ul\]\:pl-5 ul {
padding-left: 0;
padding-right: calc(var(--spacing) * 5);
}
/* --------------------------------------------------------------------------
23. CodeMirror (inside MDXEditor) - Cursor & Gutters
-------------------------------------------------------------------------- */
html[dir="rtl"] .paperclip-mdxeditor .cm-cursor,
html[dir="rtl"] .paperclip-mdxeditor .cm-dropCursor {
border-left-color: transparent !important;
border-right-color: #cdd6f4 !important;
}
html[dir="rtl"] .paperclip-mdxeditor .cm-gutters {
border-right: none !important;
border-left: 1px solid #313244 !important;
}
/* --------------------------------------------------------------------------
24. Sidebar Layout (shadcn/ui sidebar pattern)
The sidebar CSS variables are defined but the layout depends on the
app's component structure. These overrides handle common patterns.
-------------------------------------------------------------------------- */
/* If sidebar is positioned with left:0, move to right:0 */
html[dir="rtl"] [data-sidebar="sidebar"] {
left: auto;
right: 0;
}
/* Sidebar rail/resize handle */
html[dir="rtl"] [data-sidebar="rail"] {
left: auto;
right: 0;
}
/* Main content margin adjustment when sidebar present */
html[dir="rtl"] [data-sidebar="content"] {
margin-left: 0;
}
/* --------------------------------------------------------------------------
25. Dialog Overlay - full screen, no directional change needed
But ensure overlay covers correctly
-------------------------------------------------------------------------- */
html[dir="rtl"] [class*="_dialogOverlay_"] {
left: 0;
right: 0;
}
/* --------------------------------------------------------------------------
26. Radix UI Popover/Dropdown Transform Origins
These use CSS variables, so they auto-adapt. No override needed.
-------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------
27. Group rotation for collapsible triggers (chevron icons)
In RTL, a right-pointing chevron should point left by default
and rotate when open
-------------------------------------------------------------------------- */
html[dir="rtl"] .group-data-\[state\=open\]\:rotate-90:is(:where(.group)[data-state=open] *) {
rotate: -90deg;
}
/* --------------------------------------------------------------------------
28. General Hebrew typography adjustments
-------------------------------------------------------------------------- */
html[dir="rtl"] input,
html[dir="rtl"] textarea,
html[dir="rtl"] select {
text-align: right;
}
html[dir="rtl"] input[type="email"],
html[dir="rtl"] input[type="url"],
html[dir="rtl"] input[type="number"] {
text-align: left;
direction: ltr;
}
/* Placeholder text alignment */
html[dir="rtl"] input::placeholder,
html[dir="rtl"] textarea::placeholder {
text-align: right;
}
/* --------------------------------------------------------------------------
29. Scrollbar position (WebKit) - move to left side in RTL
-------------------------------------------------------------------------- */
html[dir="rtl"] ::-webkit-scrollbar {
/* Scrollbars auto-flip in RTL for most browsers */
}
/* --------------------------------------------------------------------------
30. Active SVG translate (press feedback) - flip X direction
-------------------------------------------------------------------------- */
html[dir="rtl"] [class*="_toolbarToggleItem_"]:active svg,
html[dir="rtl"] [class*="_toolbarButton_"]:active svg,
html[dir="rtl"] [class*="_actionButton_"]:active svg,
html[dir="rtl"] [class*="_tableColumnEditorTrigger_"]:active svg,
html[dir="rtl"] [class*="_tableColumnEditorToolbar_"] > button:active svg,
html[dir="rtl"] [class*="_toggleGroupRoot_"] button:active svg,
html[dir="rtl"] [class*="_addColumnButton_"]:active svg,
html[dir="rtl"] [class*="_addRowButton_"]:active svg {
transform: translate(-1px, 1px);
}
/* --------------------------------------------------------------------------
31. Responsive breakpoint overrides
-------------------------------------------------------------------------- */
@media (min-width: 640px) {
html[dir="rtl"] .sm\:flex-row {
flex-direction: row-reverse;
}
}
@media (min-width: 768px) {
html[dir="rtl"] .md\:flex-row {
flex-direction: row-reverse;
}
}
@media (min-width: 1024px) {
html[dir="rtl"] .lg\:flex-row {
flex-direction: row-reverse;
}
}
@media (min-width: 1280px) {
html[dir="rtl"] .xl\:flex-row {
flex-direction: row-reverse;
}
}
/* --------------------------------------------------------------------------
32. Fix for centered dialogs using left:50% + translate(-50%, -50%)
These should remain centered, so override left->right and flip translate
-------------------------------------------------------------------------- */
html[dir="rtl"] [class*="_dialogContent_"],
html[dir="rtl"] [class*="_largeDialogContent_"] {
left: auto;
right: 50%;
transform: translate(50%, -50%);
}
@keyframes rtl-contentShow {
0% {
opacity: 0;
transform: translate(50%, -48%) scale(.96);
}
to {
opacity: 1;
transform: translate(50%, -50%) scale(1);
}
}
html[dir="rtl"] [class*="_dialogContent_"],
html[dir="rtl"] [class*="_largeDialogContent_"] {
animation: rtl-contentShow .15s cubic-bezier(.16, 1, .3, 1);
}
/* --------------------------------------------------------------------------
33. Fix Tailwind space-x utilities
These already use margin-inline-start/end, so they should auto-adapt.
No override needed for space-x-* classes (they use logical properties).
-------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------
34. Override for the bottom-positioned elements with safe-area-inset
These are vertical-only, no RTL swap needed.
-------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------
35. Miscellaneous icon and decorative element adjustments
-------------------------------------------------------------------------- */
/* Ensure icons in buttons don't get mirrored unintentionally */
html[dir="rtl"] svg {
/* Most icons should NOT be mirrored. Only directional icons need it. */
}
/* Mirror directional icons (arrows, chevrons) */
html[dir="rtl"] [class*="chevron-left"] svg,
html[dir="rtl"] [class*="chevron-right"] svg,
html[dir="rtl"] [class*="arrow-left"] svg,
html[dir="rtl"] [class*="arrow-right"] svg {
transform: scaleX(-1);
}

918
assets/translate-he.js Normal file
View File

@@ -0,0 +1,918 @@
/**
* Paperclip AI - Hebrew Translation Injection
* Replaces English UI text with Hebrew after React renders.
* Uses MutationObserver to catch route changes and dynamic content.
*/
(function () {
"use strict";
// =========================================================================
// TRANSLATION MAP
// =========================================================================
const T = {
// --- Sidebar / Navigation ---
"Dashboard": "לוח בקרה",
"Inbox": "תיבת דואר",
"Work": "עבודה",
"Issues": "משימות",
"Routines": "שגרות",
"Goals": "יעדים",
"Company": "חברה",
"Org": "ארגון",
"Skills": "כישורים",
"Costs": "עלויות",
"Activity": "פעילות",
"Settings": "הגדרות",
"General": "כללי",
"Heartbeats": "פעימות",
"Experimental": "ניסיוני",
"Plugins": "תוספים",
"Me": "אני",
"Agents": "סוכנים",
"Projects": "פרויקטים",
"Home": "בית",
"Create": "צור",
"Not Found": "לא נמצא",
"Open sidebar": "פתח סרגל צד",
"Close sidebar": "סגור סרגל צד",
"Skip to Main Content": "דלג לתוכן הראשי",
// --- Common Buttons ---
"Save": "שמור",
"Cancel": "ביטול",
"Delete": "מחק",
"Edit": "ערוך",
"Update": "עדכן",
"Close": "סגור",
"Confirm": "אשר",
"Back": "חזור",
"Next": "הבא",
"Previous": "הקודם",
"Done": "סיום",
"Apply": "החל",
"Continue": "המשך",
"Dismiss": "סגור",
"Clear": "נקה",
"Reset": "אפס",
"Retry": "נסה שוב",
"Submit": "שלח",
"OK": "אישור",
"Yes": "כן",
"No": "לא",
"Go": "עבור",
"Search": "חיפוש",
"Filter": "סינון",
"Sort": "מיון",
"Copy": "העתק",
"Download": "הורד",
"Upload": "העלה",
"Export": "ייצוא",
"Import": "ייבוא",
"Enable": "הפעל",
"Disable": "השבת",
"Install": "התקן",
"Uninstall": "הסר התקנה",
"Start": "התחל",
"Stop": "עצור",
"Pause": "השהה",
"Resume": "חדש",
"Run": "הפעל",
"Launch": "הפעל",
"Approve": "אשר",
"Reject": "דחה",
"Archive": "ארכיון",
"Restore": "שחזר",
"Pin": "הצמד",
"Revoke": "בטל",
"Assign": "הקצה",
"Block": "חסום",
"Link": "קישור",
"View": "צפה",
"Show": "הצג",
"Hide": "הסתר",
"Open": "פתח",
"Select": "בחר",
"Remove": "הסר",
"Comment": "תגובה",
"Restart": "הפעל מחדש",
"Schedule": "תזמן",
"Configure": "הגדר",
"Choose": "בחר",
// --- Action Buttons ---
"Save changes": "שמור שינויים",
"Save Configuration": "שמור הגדרות",
"Save note": "שמור הערה",
"Save routine": "שמור שגרה",
"Save trigger": "שמור טריגר",
"Save failed": "השמירה נכשלה",
"Add Agent": "הוסף סוכן",
"Add Goal": "הוסף יעד",
"Add Project": "הוסף פרויקט",
"Add company": "הוסף חברה",
"Add item": "הוסף פריט",
"Add trigger": "הוסף טריגר",
"Add a new agent": "הוסף סוכן חדש",
"Add a skill source": "הוסף מקור כישורים",
"Add a short note": "הוסף הערה קצרה",
"Add a comment...": "הוסף תגובה...",
"Add a description...": "הוסף תיאור...",
"Add description...": "הוסף תיאור...",
"Add instructions...": "הוסף הוראות...",
"Adding...": "מוסיף...",
"Create Issue": "צור משימה",
"Create Item": "צור פריט",
"Create Account": "צור חשבון",
"Create API Key": "צור מפתח API",
"Create agent": "צור סוכן",
"Create document": "צור מסמך",
"Create goal": "צור יעד",
"Create label": "צור תווית",
"Create one": "צור אחד",
"Create one here": "צור כאן",
"Create project": "צור פרויקט",
"Create routine": "צור שגרה",
"Create skill": "צור כישור",
"Create new agent": "צור סוכן חדש",
"Create new company": "צור חברה חדשה",
"Create new issue": "צור משימה חדשה",
"Create new project": "צור פרויקט חדש",
"Create another company": "צור חברה נוספת",
"Create a new agent": "צור סוכן חדש",
"Create your Paperclip account": "צור את חשבון Paperclip שלך",
"Create your first agent": "צור את הסוכן הראשון שלך",
"Create your first company": "צור את החברה הראשונה שלך",
"Delete Company": "מחק חברה",
"Delete attachment": "מחק קובץ מצורף",
"Delete document": "מחק מסמך",
"Delete failed": "המחיקה נכשלה",
"Delete image": "מחק תמונה",
"Delete issue": "מחק משימה",
"Edit document": "ערוך מסמך",
"Edit image": "ערוך תמונה",
"Copy Agent ID": "העתק מזהה סוכן",
"Copy to clipboard": "העתק ללוח",
"Copy snippet": "העתק קטע",
"Copy document": "העתק מסמך",
"Approve CLI access": "אשר גישת CLI",
"Approve Paperclip CLI access": "אשר גישת Paperclip CLI",
"Approving...": "מאשר...",
"Archive company": "העבר חברה לארכיון",
"Assign Task": "הקצה משימה",
"Assign to me": "הקצה לי",
"Assign to requester": "הקצה למבקש",
"Import company": "ייבא חברה",
"Import complete": "הייבוא הושלם",
"Import failed": "הייבוא נכשל",
"Import preview": "תצוגה מקדימה של ייבוא",
"Import source": "מקור ייבוא",
"Export company": "ייצא חברה",
"Export downloaded": "הייצוא הורד",
"Export failed": "הייצוא נכשל",
"Install Plugin": "התקן תוסף",
"Install Example": "התקן דוגמה",
"Install update": "התקן עדכון",
"Uninstall Plugin": "הסר תוסף",
"Run now": "הפעל עכשיו",
"Run routine": "הפעל שגרה",
"Run Activity": "הפעל פעילות",
"Run Heartbeat": "הפעל פעימה",
"Open board": "פתח לוח",
"Open budgets": "פתח תקציבים",
"Open command palette": "פתח לוח פקודות",
"Open Command Palette": "פתח לוח פקודות",
"Open dashboard": "פתח לוח בקרה",
"Open docs": "פתח מסמכים",
"Open Settings": "פתח הגדרות",
"Open Issues": "משימות פתוחות",
"Open Side Panel": "פתח פאנל צדי",
"View agent": "צפה בסוכן",
"View details": "צפה בפרטים",
"View full error": "צפה בשגיאה מלאה",
"View inbox": "צפה בתיבת דואר",
"View run": "צפה בהרצה",
"Mark all as read": "סמן הכל כנקרא",
"Mark as done": "סמן כבוצע",
"Mark as read": "סמן כנקרא",
"Clear all": "נקה הכל",
"Clear repo": "נקה מאגר",
"Clear local folder": "נקה תיקיה מקומית",
"Sign in": "התחבר",
"Sign in required": "נדרשת התחברות",
"Sign in to Paperclip": "התחבר ל-Paperclip",
"Sign in / Create account": "התחבר / צור חשבון",
"Go home": "חזור לדף הבית",
"Back to all workspaces": "חזור לכל סביבות העבודה",
"Back to approvals": "חזור לאישורים",
"Back to runs": "חזור להרצות",
"Back to workspaces": "חזור לסביבות עבודה",
"Check for updates": "בדוק עדכונים",
"Start Onboarding": "התחל הקמה",
"Stop editing": "הפסק עריכה",
"Deploy to production": "פרוס לייצור",
"Restart Required": "נדרש הפעלה מחדש",
"Reset Sessions": "אפס הפעלות",
"Reset defaults": "אפס ברירות מחדל",
"Reset filters": "אפס מסננים",
"Download document": "הורד מסמך",
"Upload attachment": "העלה קובץ מצורף",
"Upload an image": "העלה תמונה",
"Upload failed": "ההעלאה נכשלה",
"Filter Bar": "סרגל סינון",
"Filter by agent name": "סנן לפי שם סוכן",
"Filter by type": "סנן לפי סוג",
"Filter skills": "סנן כישורים",
"Show properties": "הצג מאפיינים",
"Show secret": "הצג סוד",
"Hide secret": "הסתר סוד",
"Show full log": "הצג יומן מלא",
"Hide full log": "הסתר יומן מלא",
"Set repo": "הגדר מאגר",
"Change repo": "שנה מאגר",
"Submit join request": "שלח בקשת הצטרפות",
"Join request submitted": "בקשת ההצטרפות נשלחה",
"Accept bootstrap invite": "קבל הזמנת הקמה",
"Return to latest": "חזור לעדכני",
"Jump to live": "עבור לשידור חי",
"Select company": "בחר חברה",
"Select model": "בחר מודל",
"Select status": "בחר סטטוס",
"Select an option": "בחר אפשרות",
"Watch issue": "עקוב אחר משימה",
"Wake now": "העיר עכשיו",
"Keep my draft": "שמור את הטיוטה שלי",
"Keep paused": "השאר מושהה",
"Discard Draft": "מחק טיוטה",
// --- Navigation Tabs ---
"Overview": "סקירה כללית",
"Instructions": "הוראות",
"Configuration": "הגדרות",
"Runs": "הרצות",
"Budget": "תקציב",
"Workspaces": "סביבות עבודה",
"All": "הכל",
"Active": "פעיל",
"Paused": "מושהה",
"Error": "שגיאה",
"Recent": "אחרונים",
"Mine": "שלי",
"Unread": "לא נקרא",
"Status": "סטטוס",
// --- Status Labels ---
"Backlog": "רשימת המתנה",
"Todo": "לביצוע",
"In Progress": "בביצוע",
"In Review": "בסקירה",
"Planned": "מתוכנן",
"Completed": "הושלם",
"Cancelled": "בוטל",
"Critical": "קריטי",
"High": "גבוה",
"Medium": "בינוני",
"Low": "נמוך",
"Default": "ברירת מחדל",
"Blocked": "חסום",
"Queued": "בתור",
"Running": "פועל",
"Enabled": "מופעל",
"Disabled": "מושבת",
"Pending": "ממתין",
"Resolved": "נפתר",
"Unknown": "לא ידוע",
"New": "חדש",
"Archived": "בארכיון",
"Passed": "עבר",
"Failed": "נכשל",
"Managed": "מנוהל",
"Live": "חי",
"Observed": "נצפה",
"Cached": "במטמון",
"Sealed": "חתום",
"Recommended": "מומלץ",
"Beta": "בטא",
"Unlimited budget": "תקציב ללא הגבלה",
"Under budget": "בתוך התקציב",
"Out of date": "לא מעודכן",
"Up to date": "מעודכן",
"Not set.": "לא הוגדר.",
"Not installed": "לא מותקן",
// --- Thinking Effort ---
"Auto": "אוטומטי",
"Minimal": "מינימלי",
"X-High": "גבוה מאוד",
"Max": "מקסימום",
// --- Workspace Options ---
"Project default": "ברירת מחדל של פרויקט",
"New isolated workspace": "סביבת עבודה מבודדת חדשה",
"Reuse existing workspace": "שימוש חוזר בסביבה קיימת",
"Remote git repo": "מאגר git מרוחק",
"Local git checkout": "checkout מקומי של git",
"Local non-git path": "נתיב מקומי ללא git",
"Remote-managed workspace": "סביבת עבודה מנוהלת מרחוק",
"Git worktree": "Git worktree",
"Local folder": "תיקיה מקומית",
// --- Frequency Options ---
"Every minute": "כל דקה",
"Every hour": "כל שעה",
"Every day": "כל יום",
"Weekdays": "ימי חול",
"Weekly": "שבועי",
"Monthly": "חודשי",
"Custom (cron)": "מותאם אישית (cron)",
// --- Dashboard Cards ---
"Agents Enabled": "סוכנים מופעלים",
"Tasks In Progress": "משימות בביצוע",
"Month Spend": "הוצאות החודש",
"Pending Approvals": "אישורים ממתינים",
"Active Agents": "סוכנים פעילים",
"Active incidents": "תקלות פעילות",
"Active Keys": "מפתחות פעילים",
"Companies": "חברות",
"Live Runs": "הרצות חיות",
"Recent Activity": "פעילות אחרונה",
"Recent Issues": "משימות אחרונות",
"Recent Tasks": "משימות אחרונות",
"Success Rate": "אחוז הצלחה",
"Failed runs": "הרצות שנכשלו",
"Cost Summary": "סיכום עלויות",
// --- Page Titles ---
"Company Settings": "הגדרות חברה",
"Instance Settings": "הגדרות מופע",
"Approvals": "אישורים",
"Plugin Manager": "מנהל תוספים",
"Available Plugins": "תוספים זמינים",
"Installed Plugins": "תוספים מותקנים",
"Design Guide": "מדריך עיצוב",
"Org Chart": "מבנה ארגוני",
"Finance": "כספים",
"Budgets": "תקציבים",
"Budget control plane": "מישור בקרת תקציב",
"Scheduler Heartbeats": "פעימות מתזמן",
"Run Detail": "פרטי הרצה",
"Issue List": "רשימת משימות",
"Issue Properties": "מאפייני משימה",
"Execution Workspaces": "סביבות ביצוע",
"Company Packages": "חבילות חברה",
"Keyboard Shortcuts": "קיצורי מקלדת",
"Danger Zone": "אזור מסוכן",
"New Agent": "סוכן חדש",
"New Company": "חברה חדשה",
"New Issue": "משימה חדשה",
"New document": "מסמך חדש",
"New issue": "משימה חדשה",
"New project": "פרויקט חדש",
"New routine": "שגרה חדשה",
"Revision history": "היסטוריית גרסאות",
// --- Form Labels ---
"Name": "שם",
"Title": "כותרת",
"Description": "תיאור",
"Priority": "עדיפות",
"Assignee": "מוקצה",
"Project": "פרויקט",
"Labels": "תוויות",
"Created": "נוצר",
"Updated": "עודכן",
"Target Date": "תאריך יעד",
"Start date": "תאריך התחלה",
"Lead": "אחראי",
"Author": "מחבר",
"Email": "אימייל",
"Password": "סיסמה",
"Company name": "שם חברה",
"Agent name": "שם סוכן",
"Branch name": "שם ענף",
"Branch template": "תבנית ענף",
"Base ref": "הפניית בסיס",
"Repo URL": "כתובת מאגר",
"Brand color": "צבע מותג",
"Command": "פקודה",
"Adapter type": "סוג מתאם",
"Prompt Template": "תבנית פרומפט",
"Environment variables": "משתני סביבה",
"Timeout (sec)": "זמן קצוב (שניות)",
"Max concurrent runs": "הרצות מקבילות מקסימום",
"Max turns per run": "סיבובים מקסימום להרצה",
"Model": "מודל",
"Thinking effort": "מאמץ חשיבה",
"Reports to": "מדווח ל",
"Capabilities": "יכולות",
"Capabilities (optional)": "יכולות (אופציונלי)",
"Role": "תפקיד",
"Budget (USD)": "תקציב (דולר)",
"New budget (USD)": "תקציב חדש (דולר)",
"Heartbeat on interval": "פעימה במרווחי זמן",
"Wake on demand": "התעוררות לפי דרישה",
"Default model": "מודל ברירת מחדל",
"Collision strategy": "אסטרטגיית התנגשות",
"Description (optional)": "תיאור (אופציונלי)",
"Workspace name": "שם סביבת עבודה",
"Execution workspace": "סביבת ביצוע",
"Provision command": "פקודת הקמה",
"Teardown command": "פקודת פירוק",
"Cleanup command": "פקודת ניקוי",
"Runtime services": "שירותי ריצה",
"Plugin Key": "מפתח תוסף",
"Plugin ID": "מזהה תוסף",
"Key": "מפתח",
"Skill name": "שם כישור",
"Document key": "מפתח מסמך",
"Variables": "משתנים",
"Default value": "ערך ברירת מחדל",
"Source": "מקור",
"Mode": "מצב",
"Kind": "סוג",
"Type": "סוג",
"Concurrency": "מקביליות",
"Version": "גרסה",
"PID": "PID",
"Uptime": "זמן פעילות",
"Auto-Restart On": "הפעלה מחדש אוטומטית",
"AI feedback sharing": "שיתוף משוב AI",
"Feedback Sharing": "שיתוף משוב",
// --- Placeholders ---
"Search icons...": "חפש אייקונים...",
"Search issues, agents, projects...": "חפש משימות, סוכנים, פרויקטים...",
"Search issues...": "חפש משימות...",
"Search assignees...": "חפש מוקצים...",
"Search labels...": "חפש תוויות...",
"Search projects...": "חפש פרויקטים...",
"Search models...": "חפש מודלים...",
"Search files...": "חפש קבצים...",
"Search inbox\u2026": "חפש בתיבת דואר\u2026",
"Type a command or search...": "הקלד פקודה או חפש...",
"Issue title": "כותרת משימה",
"Project name": "שם פרויקט",
"Target date": "תאריך יעד",
"Goal title": "כותרת יעד",
"Leave a comment...": "השאר תגובה...",
"Write something...": "כתוב משהו...",
"Enter a name": "הזן שם",
"Describe...": "תאר...",
"Choose a value": "בחר ערך",
"Choose frequency...": "בחר תדירות...",
"Category": "קטגוריה",
"Approval status": "סטטוס אישור",
"Routine title": "כותרת שגרה",
"Optional title": "כותרת אופציונלית",
"Markdown body": "גוף Markdown",
"New label": "תווית חדשה",
"Short description": "תיאור קצר",
"Optional company description": "תיאור חברה אופציונלי",
"Execution workspace name": "שם סביבת ביצוע",
"NPM Package Name": "שם חבילת NPM",
// --- Empty States ---
"No results found.": "לא נמצאו תוצאות.",
"No issues": "אין משימות",
"No labels": "אין תוויות",
"No goal": "אין יעד",
"No project": "אין פרויקט",
"No parent": "אין הורה",
"No manager": "אין מנהל",
"No assignee": "אין מוקצה",
"No value": "אין ערך",
"No default": "אין ברירת מחדל",
"No runs yet": "אין הרצות עדיין",
"No runs yet.": "אין הרצות עדיין.",
"No revisions yet": "אין גרסאות עדיין",
"No icons match": "לא נמצאו אייקונים",
"No agents match the selected filter.": "אין סוכנים שתואמים למסנן שנבחר.",
"No agents attached": "לא צורפו סוכנים",
"No active API keys.": "אין מפתחות API פעילים.",
"No activity yet.": "אין פעילות עדיין.",
"No plugins installed": "לא הותקנו תוספים",
"No goals.": "אין יעדים.",
"No tasks yet.": "אין משימות עדיין.",
"No triggers configured yet.": "לא הוגדרו טריגרים עדיין.",
"No description": "אין תיאור",
"No diagnostics": "אין אבחנות",
"No company selected": "לא נבחרה חברה",
"No cost data yet.": "אין נתוני עלויות עדיין.",
"No cost events yet.": "אין אירועי עלות עדיין.",
"No items added yet.": "לא נוספו פריטים עדיין.",
"No recent agent runs.": "אין הרצות סוכנים אחרונות.",
"No recent issues.": "אין משימות אחרונות.",
"No sub-goals.": "אין תת-יעדים.",
"No sub-issues.": "אין תת-משימות.",
"None": "ללא",
"Unassigned": "לא מוקצה",
"You have no agents.": "אין לך סוכנים.",
"No unsaved changes.": "אין שינויים שלא נשמרו.",
"No configuration options available.": "אין אפשרויות הגדרה זמינות.",
// --- Loading States ---
"Loading...": "טוען...",
"Loading\u2026": "טוען\u2026",
"Loading companies...": "טוען חברות...",
"Loading plugins...": "טוען תוספים...",
"Loading revisions...": "טוען גרסאות...",
"Loading workspaces...": "טוען סביבות עבודה...",
"Loading workspace\u2026": "טוען סביבת עבודה\u2026",
"Loading run logs...": "טוען יומני הרצה...",
"Loading log...": "טוען יומן...",
"Loading keys...": "טוען מפתחות...",
"Loading invite...": "טוען הזמנה...",
"Loading plugin details...": "טוען פרטי תוסף...",
"Checking health...": "בודק תקינות...",
"Uploading logo...": "מעלה לוגו...",
"Building export...": "בונה ייצוא...",
// --- Toast & Notifications ---
"Copied!": "הועתק!",
"Saved": "נשמר",
"Approval confirmed": "האישור אושר",
"CLI access approved": "גישת CLI אושרה",
"Bootstrap complete": "ההקמה הושלמה",
"Action failed": "הפעולה נכשלה",
"Comment failed": "התגובה נכשלה",
"Copy failed": "ההעתקה נכשלה",
"Archive failed": "הארכוב נכשל",
"Approve failed": "האישור נכשל",
"Reject failed": "הדחייה נכשלה",
"Update failed": "העדכון נכשל",
"Routine created": "השגרה נוצרה",
"Routine saved": "השגרה נשמרה",
"Routine run failed": "הרצת השגרה נכשלה",
"Routine run started": "הרצת השגרה החלה",
"Skill created": "הכישור נוצר",
"Skill saved": "הכישור נשמר",
"Skill updated": "הכישור עודכן",
"Plugin installed successfully": "התוסף הותקן בהצלחה",
"Plugin uninstalled successfully": "התוסף הוסר בהצלחה",
"Plugin enabled": "התוסף הופעל",
"Plugin disabled": "התוסף הושבת",
"Plugin error": "שגיאת תוסף",
"Trigger added": "הטריגר נוסף",
"Trigger deleted": "הטריגר נמחק",
"Trigger saved": "הטריגר נשמר",
"Run completed": "ההרצה הושלמה",
"Run failed": "ההרצה נכשלה",
"Command failed": "הפקודה נכשלה",
"Invite not available": "ההזמנה לא זמינה",
"Invite not found": "ההזמנה לא נמצאה",
"Interrupt requested": "התבקשה הפסקה",
"Interrupt failed": "ההפסקה נכשלה",
// --- Error Messages ---
"An error was thrown.": "אירעה שגיאה.",
"Authentication failed": "ההתחברות נכשלה",
"Unknown error": "שגיאה לא ידועה",
"Invalid JSON.": "JSON לא תקין.",
"Failed to create issue. Try again.": "יצירת המשימה נכשלה. נסה שוב.",
"Failed to create company": "יצירת החברה נכשלה",
"Failed to create agent": "יצירת הסוכן נכשלה",
"Failed to save": "השמירה נכשלה",
"Failed to approve": "האישור נכשל",
"Failed to reject": "הדחייה נכשלה",
"Instance setup required": "נדרשת הגדרת מופע",
// --- Dialog & Confirmation ---
"Delete this company and all its data? This cannot be undone.": "למחוק חברה זו וכל הנתונים שלה? לא ניתן לבטל פעולה זו.",
"Delete this document? This cannot be undone.": "למחוק מסמך זה? לא ניתן לבטל פעולה זו.",
"Mark all as read?": "לסמן הכל כנקרא?",
"Already have an account?": "כבר יש לך חשבון?",
// --- Section Headings ---
"Actions": "פעולות",
"Advanced": "מתקדם",
"Alerts": "התראות",
"Appearance": "מראה",
"Attachments": "קבצים מצורפים",
"Automation": "אוטומציה",
"Categories": "קטגוריות",
"Cleanup": "ניקוי",
"Codebase": "בסיס קוד",
"Comments": "תגובות",
"Company skills": "כישורי חברה",
"Config": "הגדרות",
"Context": "הקשר",
"Controls": "בקרה",
"Credits": "זיכויים",
"Debits": "חיובים",
"Details": "פרטים",
"Documents": "מסמכים",
"Environment": "סביבה",
"Error Details": "פרטי שגיאה",
"Files": "קבצים",
"Filters": "מסננים",
"Git status": "סטטוס Git",
"Hiring": "גיוס",
"Identity": "זהות",
"Invites": "הזמנות",
"Invocation": "הפעלה",
"Join requests": "בקשות הצטרפות",
"Lifecycle": "מחזור חיים",
"Linked Issues": "משימות מקושרות",
"Linked issues": "משימות מקושרות",
"Options": "אפשרויות",
"Output": "פלט",
"Owner": "בעלים",
"Permissions": "הרשאות",
"Properties": "מאפיינים",
"Providers": "ספקים",
"Quick filters": "מסננים מהירים",
"Recent financial events": "אירועי כספים אחרונים",
"Recent operations": "פעולות אחרונות",
"Remaining": "נותר",
"Result": "תוצאה",
"Revoked Keys": "מפתחות שבוטלו",
"Runtime services": "שירותי ריצה",
"Selected skills": "כישורים נבחרים",
"Summary": "סיכום",
"Tokens": "אסימונים",
"Unsaved changes": "שינויים שלא נשמרו",
"Warnings": "אזהרות",
"Workspace context": "הקשר סביבת עבודה",
"Worktree": "עץ עבודה",
// --- Table/List Headers ---
"Action": "פעולה",
"Agent": "סוכן",
"Branch": "ענף",
"Cost": "עלות",
"Created by": "נוצר על ידי",
"Current": "נוכחי",
"Current state": "מצב נוכחי",
"Date": "תאריך",
"Input tokens": "אסימוני קלט",
"Output tokens": "אסימוני פלט",
"Cached tokens": "אסימונים ממטמון",
"Last Crash": "קריסה אחרונה",
"Last run": "הרצה אחרונה",
"Overall": "כולל",
"Requested by": "התבקש על ידי",
"Task": "משימה",
"Total": "סך הכל",
"Total cost": "עלות כוללת",
"Used by": "בשימוש על ידי",
// --- Misc UI Labels ---
"About": "אודות",
"Adapter": "מתאם",
"After": "אחרי",
"Before": "לפני",
"Board view": "תצוגת לוח",
"Coming soon": "בקרוב",
"Documentation": "תיעוד",
"Earlier": "מוקדם יותר",
"Estimated": "משוער",
"External": "חיצוני",
"Human": "אנושי",
"My recent issues": "המשימות האחרונות שלי",
"Optional": "אופציונלי",
"Per issue": "למשימה",
"Per run": "להרצה",
"Secret": "סוד",
"See All \u2192": "ראה הכל \u2192",
"Small": "קטן",
"Large": "גדול",
"User": "משתמש",
"View details \u2192": "צפה בפרטים \u2192",
"All approval statuses": "כל סטטוסי האישור",
"All categories": "כל הקטגוריות",
"All providers": "כל הספקים",
"All types": "כל הסוגים",
"All Time": "כל הזמנים",
"Last 14 days": "14 ימים אחרונים",
"Automation enabled.": "אוטומציה מופעלת.",
"Automation paused.": "אוטומציה מושהית.",
"Mobile navigation": "ניווט נייד",
// --- Command Palette ---
"Pages": "דפים",
// --- Onboarding ---
"Bootstrap your Paperclip instance": "הקם את מופע ה-Paperclip שלך",
"Name your company": "תן שם לחברה שלך",
"This is the organization your agents will work for.": "זהו הארגון שעבורו הסוכנים שלך יעבדו.",
"This will be the CEO": "זה יהיה המנכ\"ל",
"Give it something to do": "תן לו משהו לעשות",
"Ready to launch": "מוכן להפעלה",
"Welcome to Paperclip. Set up your first company and agent to get started.": "ברוכים הבאים ל-Paperclip. הקם את החברה והסוכן הראשונים שלך כדי להתחיל.",
"I want advanced configuration myself": "אני רוצה להגדיר בעצמי הגדרות מתקדמות",
"Get started by creating a company.": "התחל ביצירת חברה.",
// --- Descriptions ---
"Choose how this agent will run tasks.": "בחר כיצד סוכן זה יריץ משימות.",
"Choose your adapter type for advanced setup.": "בחר את סוג המתאם להגדרה מתקדמת.",
"Plugins are alpha.": "התוספים בשלב אלפא.",
"Read our terms of service": "קרא את תנאי השירות שלנו",
"Select a company first.": "בחר חברה תחילה.",
"Sharing is currently disabled.": "השיתוף מושבת כרגע.",
"Don't allow": "אל תאפשר",
"Always allow": "אפשר תמיד",
"What could have been better?": "מה יכול היה להיות טוב יותר?",
"Drop image to upload": "גרור תמונה להעלאה",
// --- Aria Labels ---
"breadcrumb": "ניווט",
"toggle menu": "החלפת תפריט",
"Instance settings": "הגדרות מופע",
"Scroll to bottom": "גלול למטה",
"Change project color": "שנה צבע פרויקט",
"Zoom in": "הגדל",
"Zoom out": "הקטן",
"Fit chart to screen": "התאם תרשים למסך"
};
// =========================================================================
// TRANSLATION ENGINE
// =========================================================================
// Build a reverse lookup for performance: lowercase -> original key
const lowerMap = {};
for (const key in T) {
lowerMap[key.toLowerCase()] = key;
}
/**
* Translate a text string if a translation exists.
* Uses exact match first, then case-insensitive.
*/
function translate(text) {
const trimmed = text.trim();
if (!trimmed) return null;
// Exact match
if (T[trimmed] !== undefined) return T[trimmed];
// Case-insensitive match
const lk = trimmed.toLowerCase();
if (lowerMap[lk]) return T[lowerMap[lk]];
return null;
}
/**
* Check if a node is likely user-generated content (should NOT be translated).
* This includes agent names, issue titles in dynamic lists, etc.
*/
function isUserContent(node) {
let el = node.parentElement;
while (el) {
// Skip elements with contenteditable (editors)
if (el.contentEditable === "true") return true;
// Skip code blocks
const tag = el.tagName;
if (tag === "CODE" || tag === "PRE") return true;
// Skip markdown rendered content body (but not headings/labels)
if (el.classList && el.classList.contains("paperclip-markdown")) return true;
// Skip elements explicitly marked as user content
if (el.dataset && el.dataset.noTranslate === "true") return true;
el = el.parentElement;
}
return false;
}
/**
* Walk all text nodes under a root and translate them.
*/
function translateTextNodes(root) {
const walker = document.createTreeWalker(
root,
NodeFilter.SHOW_TEXT,
null
);
let node;
while ((node = walker.nextNode())) {
if (isUserContent(node)) continue;
const original = node.nodeValue;
const translated = translate(original);
if (translated !== null && translated !== original) {
node.nodeValue = translated;
}
}
}
/**
* Translate placeholder and aria-label attributes.
*/
function translateAttributes(root) {
// Placeholders
const inputs = root.querySelectorAll("input[placeholder], textarea[placeholder]");
inputs.forEach(function (el) {
const t = translate(el.placeholder);
if (t) el.placeholder = t;
});
// aria-labels
const ariaEls = root.querySelectorAll("[aria-label]");
ariaEls.forEach(function (el) {
const t = translate(el.getAttribute("aria-label"));
if (t) el.setAttribute("aria-label", t);
});
// title attributes
const titleEls = root.querySelectorAll("[title]");
titleEls.forEach(function (el) {
const t = translate(el.getAttribute("title"));
if (t) el.setAttribute("title", t);
});
}
/**
* Full translation pass on the entire document.
*/
function translateAll() {
translateTextNodes(document.body);
translateAttributes(document.body);
// Translate page title
if (document.title) {
const t = translate(document.title);
if (t) document.title = t;
}
}
// =========================================================================
// MUTATION OBSERVER - catch route changes and dynamic content
// =========================================================================
let translateTimer = null;
function scheduleTranslation() {
if (translateTimer) return;
translateTimer = setTimeout(function () {
translateTimer = null;
translateAll();
}, 50);
}
// Observe #root for changes (React renders here)
function startObserver() {
const root = document.getElementById("root");
if (!root) return;
const observer = new MutationObserver(function (mutations) {
let hasTextChange = false;
for (let i = 0; i < mutations.length; i++) {
const m = mutations[i];
if (m.type === "childList" && m.addedNodes.length > 0) {
hasTextChange = true;
break;
}
if (m.type === "characterData") {
hasTextChange = true;
break;
}
}
if (hasTextChange) {
scheduleTranslation();
}
});
observer.observe(root, {
childList: true,
subtree: true,
characterData: true
});
// Also observe body for Radix portals (dialogs, tooltips, dropdowns)
const bodyObserver = new MutationObserver(function (mutations) {
for (let i = 0; i < mutations.length; i++) {
const m = mutations[i];
if (m.type === "childList") {
for (let j = 0; j < m.addedNodes.length; j++) {
const node = m.addedNodes[j];
if (node.nodeType === 1 && node !== root) {
translateTextNodes(node);
translateAttributes(node);
}
}
}
}
});
bodyObserver.observe(document.body, {
childList: true,
subtree: false
});
}
// =========================================================================
// INIT
// =========================================================================
function init() {
// Wait for React to render
setTimeout(function () {
translateAll();
startObserver();
}, 300);
// Additional pass after longer delay for lazy-loaded content
setTimeout(translateAll, 1500);
setTimeout(translateAll, 4000);
}
// Start when DOM is ready
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init);
} else {
init();
}
})();