# איפיון — גרסה 0.0.1

> סקריפט Tampermonkey שמתרגם את הממשק של `github.com` לעברית.
> שם העבודה: **github-he** (ניתן לשנות, ראו סעיף 14).

---

## 0. מטרות ולא-מטרות

### מטרות של 0.0.1
1. להחליף טקסט ממשק באנגלית בעברית ב-`github.com`, בדפים המרכזיים (סעיף 11).
2. מילון חיצוני לקוד: אפשר לעדכן תרגומים בלי לגעת בלוגיקה.
3. טקסט עברי בכיוון RTL, **בלי** להפוך את פריסת האתר ובלי יישור לימין.
4. **מצב איסוף** שמגלה מחרוזות שעוד לא תורגמו, בצורה בטוחה לפרטיות.
5. מונחי GitHub נשארים באנגלית (commit, pull request, issue, branch, repository, fork וכו').
6. עובד ב-NetFree: הסקריפט לא מבצע שום בקשת רשת בזמן ריצה.

### לא-מטרות של 0.0.1
- תרגום תוכן משתמשים (issues, הערות, README, קוד, הודעות commit).
- תרגום אוטומטי/מכונה בתוך הסקריפט.
- שפות נוספות (הארכיטקטורה תאפשר, אבל לא ממומש).
- GitHub Enterprise Server, `gist.github.com`, `docs.github.com`, GitHub Pages.
- ממשק גרפי לביקורת מחרוזות (יהיה ב-0.1).
- תרגום משפטים שמורכבים מכמה אלמנטים (למשל "user opened this issue 3 days ago") — ראו סעיף 2.5.

---

## 1. ארכיטקטורה בגדול

```
 ┌──────────────────────────── דפדפן (Tampermonkey) ────────────────────────────┐
 │                                                                                │
 │   github.com  ──DOM──►  Engine (TreeWalker + MutationObserver)                 │
 │                            │            │                                      │
 │                            ▼            ▼                                      │
 │                       Skip rules    Dictionary lookup ──► exact / template     │
 │                            │            │                                      │
 │                            ▼            ▼                                      │
 │                       (לא נוגעים)   החלפת nodeValue + סימון RTL                │
 │                                         │                                      │
 │                                         └──► לא נמצא תרגום ──► Collector       │
 │                                                                  │             │
 │                                       GM_setValue (אחסון מקומי) ◄┘             │
 └────────────────────────────────────────────────────────────────────────────────┘
                                    │  ייצוא ידני (קובץ JSON)
                                    ▼
        tools/merge_collected  →  pending.json  →  תרגום  →  reviewed.json  →  build  →  github-he.user.js
```

עיקרון מנחה: **הסקריפט לא יוצא לרשת**. המילון נבנה לתוך קובץ ה-`.user.js` בזמן הבנייה.

---

## 2. מנגנון ההחלפה — איך יודעים איזה משפט מתורגם לאיזה

### 2.1 העיקרון: המחרוזת האנגלית היא המפתח
כמו ב-gettext (`msgid`): אין מזהים. המילון הוא מיפוי **טקסט אנגלי מדויק ← טקסט עברי**.

```json
{
  "Pull requests": "Pull requests",
  "Merge pull request": "ביצוע merge ל-pull request",
  "Sign in": "כניסה",
  "{n} commits": "{n} commits",
  "1 commit": "commit אחד"
}
```

### 2.2 נרמול לפני חיפוש
לכל צומת טקסט (`Text node`):
1. שומרים את הרווחים בתחילת הצומת ובסופו (GitHub מוסיף שורות חדשות והזחות).
2. מחשבים מפתח: `key = text.replace(/\s+/g, ' ').trim()`.
3. מחליפים NBSP ותווי רוחב-אפס ברווח רגיל לצורך החיפוש בלבד.
4. אחרי החלפה: `nodeValue = leadingWS + hebrew + trailingWS`.

### 2.3 סדר החיפוש (עדיפות יורדת)
1. **התאמה מדויקת מוגבלת-הקשר** — רשומה עם `scope` (ביטוי רגולרי על נתיב ה-URL או סלקטור). לפתרון דו-משמעות ("Open" כפועל מול "Open" כמצב).
2. **התאמה מדויקת גלובלית** — `Map.get(key)`, O(1).
3. **תבנית** — מפתחות עם `{n}` / `{1}` / `{2}`:
   - `{n}` = מספר: `(\d[\d,.]*[kKmM]?)`
   - `{1}`,`{2}`… = טקסט חופשי קצר: `(.+?)`
   - מקומפל פעם אחת ל-RegExp מעוגן (`^…$`), מאונדקס לפי המילה הארוכה ביותר שבו כדי שלא נריץ את כל התבניות על כל צומת.
   - בעברית ממלאים: `"{n} commits"` → `"{n} commits"`, `"Merged {1} into {2}"` → `"בוצע merge של {1} אל {2}"`.
4. לא נמצא → מעבירים ל-Collector (אם מופעל) ולא נוגעים בטקסט.

**החלפה רק על התאמה של כל הצומת.** לא מחליפים חלקי מחרוזות ולא מילה-במילה. זה מונע שיבוש של טקסטים שלא מכירים.

### 2.4 יחיד / רבים
שתי רשומות נפרדות, והמדויקת גוברת על התבנית:
```
"1 commit"   → "commit אחד"
"{n} commits"→ "{n} commits"
```

### 2.5 משפטים שמורכבים מכמה אלמנטים (מחוץ ל-0.0.1)
דוגמה: `<a>octocat</a> opened this issue 3 days ago`. כאן הטקסט מפוצל בין צמתים. ב-0.0.1 נתרגם רק את הצמתים הקבועים ("opened this issue"); הצמתים הדינמיים (שם משתמש, תאריך) יישארו כפי שהם. ב-0.1 נוסיף **תבניות שלד** (skeleton): מחברים את הטקסט עם סימון `{0}` לכל אלמנט-ילד, מתרגמים, ומחלקים חזרה.

### 2.6 תכונות (attributes) שמתורגמות
`placeholder`, `aria-label`, `title`, `alt`, `data-disable-with`, `data-confirm`, וגם `value` רק עבור `input[type=submit|button|reset]`.
אותו מילון, אותו אלגוריתם. נצפה ב-`MutationObserver` עם `attributeFilter`.

### 2.7 כותרת הדף (`document.title`)
GitHub מרכיב כותרות כמו `Pull requests · owner/repo`. מפצלים לפי ` · ` ו-` - `, מתרגמים כל קטע בנפרד, ומחברים חזרה. שמות repo לא יימצאו במילון ולכן יישארו.

### 2.8 מנגנון DOM
- **ריצה:** `@run-at document-start` כדי להתחיל לפני שהדף מצויר ולמנוע הבהוב אנגלית.
- **מעבר ראשוני:** `TreeWalker` על `SHOW_TEXT`, עם דילוג על אזורי skip (סעיף 5).
- **שינויים דינמיים:** `MutationObserver` על `document.documentElement` (`childList`, `subtree`, `characterData`). אוספים רק צמתים שנוספו/שונו לתור, ומעבדים בקבוצות (`requestIdleCallback` / `requestAnimationFrame`, מקסימום ~8ms לכל קבוצה).
- **ניווט בלי רענון:** מאזינים ל-`turbo:render`, `turbo:load`, `soft-nav:end`, `pjax:end` ולוחצים מחדש על מעבר מלא על הדף.
- **Shadow DOM:** סורקים `element.shadowRoot` פתוחים ומחברים להם Observer נפרד.
- **מניעת לולאות:** שומרים `WeakMap<Node, {src, out}>`. אם `node.nodeValue === out` — מדלגים. אם GitHub/React כתב אנגלית חדשה לאותו צומת — מתרגמים שוב.
- **שינוי `nodeValue` בלבד:** לא עוטפים צמתים באלמנטים ולא מחליפים צמתים, כדי לא לשבור Frameworks (React) שמחזיקים הפניה לצומת.
- **תיעוד המקור:** `WeakMap` שומר את הטקסט המקורי (לאיסוף, לדיבוג ולתצוגה "הצג מקור").

### 2.9 תאריכים יחסיים
GitHub משתמש באלמנטים `relative-time` / `time-ago`. נבדוק אם הוספת `lang="he"` גורמת להם להציג עברית דרך `Intl`. **להוכיח בבדיקה** לפני שמתחייבים; אם לא עובד — נעביר ל-0.1.

---

## 3. המילון

### 3.1 פורמט זמן ריצה (`dict/he.json`, נוצר בבנייה)
```json
{
  "meta": { "lang": "he", "version": "0.0.1", "built": "2026-10-12", "count": 0 },
  "strings": { "Sign in": "כניסה" },
  "scoped": [
    { "en": "Open", "he": "פתוח", "scope": { "selector": ".State" } }
  ]
}
```
מפתח שמכיל `{n}`/`{1}`… נחשב **תבנית** אוטומטית.

### 3.2 מקורות המילון (`dict/sources/`) — לפי עדיפות
| קובץ | תוכן | סטטוס |
|---|---|---|
| `manual.json` | תרגומים שנכתבו/תוקנו ידנית | גובר על הכול |
| `reviewed.json` | מחרוזות שנאספו ותורגמו ואושרו | מהימן |
| `seed-app.json` | זוגות אנגלית-עברית מהתרגום של אפליקציית האנדרואיד | **לא מאושר** — רק התאמה מדויקת למחרוזת קיימת באתר |

### 3.3 זרע מהאפליקציה
- ייצוא מהעבודה שכבר נעשתה (כ-3,700 מחרוזות + רבים).
- המרת פלייסהולדרים: `%s`→`{1}`, `%1$s`→`{1}`, `%2$s`→`{2}`, `%d`→`{n}`.
- **אין הנחה לגבי החפיפה** עם הממשק של האתר; היא תימדד אחרי הסריקה הראשונה (סעיף 6.8). רשומות זרע משמשות רק כשהאנגלית זהה לחלוטין.

### 3.4 כללי תרגום (קובץ `docs/GLOSSARY.md`)
- נשארים באנגלית: commit, pull request (PR), issue, branch, repository (repo), fork, merge, rebase, squash, review, workflow, release, tag, gist, milestone, label, assignee, discussion, project, Actions, Copilot, Dependabot, Codespaces, README, diff, star/unstar (בפועל "כוכב").
- פנייה: **לשון רבים, ניטרלית** ("לחצו", "בחרו") — כמו באפליקציה.
- שמות פעולה: שם-פעולה ("ביצוע", "יצירת", "הוספת"), לא פועל מצווה, בכפתורים ובתפריטים.
- ללא ניקוד. סימני הפיסוק נשמרים כמו במקור האנגלי.
- שמות מוצרים/תכונות של GitHub — באנגלית כשהם שם עצם פרטי.

### 3.5 בדיקות אוטומטיות על המילון (CI)
- JSON תקין, בלי מפתחות כפולים.
- זהות פלייסהולדרים בין המקור לתרגום (`{n}`, `{1}`…).
- אין ערכים ריקים; אין רווחים בקצה.
- התראה (לא כישלון) אם התרגום ארוך מפי 2.5 מהמקור.
- אין תווי בקרה (U+202A–U+202E, U+2066–U+2069) בערכי המילון — את אלה מוסיף הסקריפט.

---

## 4. כיווניות (RTL)

הדרישה: **הטקסט העברי מימין לשמאל, בלי יישור לימין ובלי להפוך את הפריסה.**

### 4.1 למה זה לא טריוויאלי
משפט עברי עם מונחים באנגלית ("ביצוע commit לשינויים") מוצג נכון רק כשכיוון הפסקה הבסיסי הוא RTL. אבל `direction: rtl` על אלמנט שמכיל גם אייקון (SVG) וגם טקסט **הופך את סדר הילדים** ב-Flex ומזיז את האייקון לצד הלא נכון.

### 4.2 הפתרון — מצב `auto` (ברירת מחדל)
| סוג הצומת | טיפול |
|---|---|
| צומת טקסט שהאלמנט-אב שלו הוא **עלה** (אין לו ילדי-אלמנט) | מוסיפים לאב את התכונה `data-ghe` ו-CSS: `[data-ghe]{direction:rtl;unicode-bidi:isolate;text-align:left}` |
| צומת טקסט שיש לאב שלו אחים מסוג אלמנט (אייקון, badge) | עוטפים את הערך במקום ב-DOM: `U+2067` (RLI) + עברית + `U+2069` (PDI) |

תוצאה: הטקסט RTL, האלמנט נשאר במקומו, וכל שאר הדף נשאר LTR. היישור נשאר שמאלי.

### 4.3 מצבים נוספים (הגדרה)
- `css` — רק תכונה + CSS (עלול להזיז אייקונים).
- `isolate` — רק תווי RLI/PDI בכל מקום.
- `off` — בלי טיפול בכיוון.

### 4.4 מגבלות ידועות
- תווי בקרה בטקסט משפיעים על **העתקה**, על **חיפוש בדף (Ctrl+F)** ועל סקריפטים של GitHub שקוראים `textContent` להשוואה. לכן מצב ה-`isolate` מוחל רק בצמתים שאינם עלים.
- `text-overflow: ellipsis` ב-`direction: rtl` חותך מצד שמאל. נבדוק ב-0.0.1 ונתקן אם נתקלים בכך.
- מספרים ותווי פיסוק בקצה המשפט עשויים להתמקם "הפוך"; מטפלים ברשומות בודדות לפי צורך.

---

## 5. אזורים שאסור לגעת בהם (Skip rules)

### 5.1 אלמנטים
`script`, `style`, `noscript`, `template`, `svg`, `textarea`, `pre`, `code`, `kbd`, `samp`, `[contenteditable]`, `input` (ערך שהמשתמש הקליד), `select option[value]` של משתמשים.

### 5.2 תוכן משתמשים (סלקטורים; יתעדכנו עם הזמן)
- `.markdown-body`, `.comment-body`, `.js-comment-body`
- `.blob-code`, `.react-code-lines`, `.diff-table`, `.react-blob-print-hide`
- `.markdown-title`, `.js-issue-title`, `[data-testid="issue-title"]`
- שמות repo/משתמש/branch/תג: `a[data-hovercard-url]`, `.css-truncate-target`, `.branch-name`, `[itemprop="name"]`
- `.commit-message`, `.commit-title`, `[data-testid="commit-message"]`

### 5.3 למה זה לא מספיק לבד
המילון מדויק (התאמה מלאה בלבד), ולכן הסיכון נמוך: רק טקסט שזהה **בדיוק** למחרוזת ממשק יוחלף. הסלקטורים נועדו לטפל במקרים שבהם תוכן משתמש במקרה שווה לטקסט ממשק (issue שנקרא "Settings").

---

## 6. מנגנון האיסוף (Collector)

### 6.1 מה הוא עושה
כשמצב איסוף פעיל, כל צומת טקסט/תכונה שעובר את בדיקות ה-skip **ולא נמצא לו תרגום** נרשם כמועמד לתרגום.

### 6.2 איפה המילים נשמרות
**מקומית בלבד**, באחסון של Tampermonkey (`GM_setValue("ghe.collected", …)`). שום דבר לא נשלח לשום מקום. מהאחסון מייצאים ידנית.

### 6.3 מבנה רשומה
```json
{
  "en": "Create new file",
  "kind": "text",
  "seen": 7,
  "pages": ["/:owner/:repo", "/:owner/:repo/tree/:ref"],
  "ctx": "BUTTON.btn",
  "first": "2026-10-12",
  "last": "2026-10-12"
}
```
- `kind`: `text` או `attr:placeholder` / `attr:aria-label` וכו'.
- `pages`: **תבניות נתיב**, לא כתובות אמיתיות (`/octocat/Hello-World` → `/:owner/:repo`).
- `ctx`: תג + מחלקה ראשונה של האב, לסיוע בדו-משמעות. בלי תוכן.

### 6.4 פרטיות — מה ההגנות
1. **לא אוספים מ-repos פרטיים:** אם `meta[name="octolytics-dimension-repository_public"]` הוא `false` — האיסוף כבוי בדף.
2. **רק אזורים שאינם skip** (סעיף 5).
3. **סף חזרתיות:** מחרוזת נשמרת רק אחרי שנראתה **לפחות 3 פעמים ב-2 תבניות נתיב שונות לפחות**. תוכן משתמשים בדרך כלל ייחודי; ממשק חוזר.
4. **נרמול:** מספרים → `{n}`; טקסט שנראה כשם משתמש/repo/branch (מכיל `/` בלי רווח, מתחיל ב-`@`, או בתוך קישור hovercard) — לא נשמר.
5. **אורך:** מחרוזות מעל 120 תווים לא נשמרות כברירת מחדל.
6. **אפשרות ניקוי:** פקודת תפריט "מחיקת כל מה שנאסף".
7. **תצוגה מקדימה לפני ייצוא:** בקונסולה, ובגרסה 0.1 בחלון.

### 6.5 הזרימה מקצה לקצה
```
גלישה באתר (איסוף פעיל)
   → אחסון מקומי (עם סף חזרתיות)
   → פקודת תפריט "ייצוא נאסף" → github-he-collected-YYYYMMDD.json
   → tools/merge_collected.mjs: איחוד קבצים, ניקוי כפילויות, סינון מה שכבר במילון
   → dict/sources/pending.json  (רק אנגלית; ערך עברי ריק)
   → תרגום בסבבים (בדיוק כמו שעשינו באפליקציה) → reviewed.json
   → build → dist/github-he.user.js
```

### 6.6 תרומה של אחרים
- תבנית Issue בשם "מחרוזות שנאספו" לצירוף קובץ הייצוא.
- כל הקבצים עוברים `merge_collected` ומתווספים כמועמדים; שום דבר לא נכנס למילון בלי תרגום וסקירה.

### 6.7 מצב דיבוג / סטטיסטיקות
חשיפת אובייקט `unsafeWindow.GHE`:
- `GHE.stats()` — כמה צמתים תורגמו / לא תורגמו בדף הנוכחי.
- `GHE.untranslated()` — רשימת המחרוזות הלא-מתורגמות שבדף (לא נשמרות).
- `GHE.reload()` — מעבר מחדש על הדף.

### 6.8 כיסוי נמדד
`כיסוי = מתורגם / (מתורגם + לא מתורגם במועמדים מותרים)`. זהו המדד של קריטריוני הקבלה (סעיף 11).

---

## 7. קבצי ZIM וחילוץ אופליין (כלי עזר, לא חלק מהסקריפט)

**מה זה עוזר:** קובץ ZIM (פורמט Kiwix) הוא ארכיון של דפי HTML. אפשר להריץ עליו כלי חילוץ שקורא את הדפים, מוציא את צמתי הטקסט לפי אותם כללי skip, ומפיק קובץ `pending.json` בלי לגלוש.

**מגבלות כנות:**
- ארכיון ZIM של GitHub תופס רק דפים **ציבוריים** שנסרקו ללא התחברות; חסרים בו הגדרות, התראות, דשבורד אישי ומסכים שנטענים ב-JS.
- ארכיון של `docs.github.com` נותן מונחי תיעוד ולא את הממשק עצמו.

**לכן:** ב-0.0.1 מצב האיסוף בדפדפן הוא המקור העיקרי. כלי החילוץ (`tools/extract_from_zim.py`, עם `python-libzim`, ו-`tools/extract_from_html.mjs` לקבצי "שמור דף בשם") ייכנס כ-**שלב מקביל**, ורק אם מוכיח עצמו בסריקה הראשונה.

---

## 8. ממשק משתמש (בלי UI בתוך הדף ב-0.0.1)

פקודות בתפריט Tampermonkey (`GM_registerMenuCommand`):
| פקודה | פעולה |
|---|---|
| תרגום: מופעל / כבוי | החלפה; דורש רענון |
| איסוף מחרוזות: מופעל / כבוי | |
| ייצוא מחרוזות שנאספו | הורדת JSON |
| מחיקת מחרוזות שנאספו | |
| כיווניות: auto / css / isolate / off | |
| ייבוא מילון אישי | קובץ JSON; גובר על המילון המובנה |
| איפוס הגדרות | |

הגדרות נשמרות ב-`GM_setValue` תחת המפתחות `ghe.enabled`, `ghe.collect`, `ghe.rtlMode`, `ghe.userDict`, `ghe.collected`.

קיצור מקלדת: `Alt+Shift+H` — החלפה זמנית לאנגלית לצפייה במקור (דורש רענון ב-0.0.1).

---

## 9. מבנה המאגר

```
github-he/
├─ README.md                  # עברית: התקנה, שימוש, תרומה
├─ README.en.md
├─ LICENSE                    # MIT (הצעה)
├─ package.json               # build, test, lint
├─ src/
│  ├─ main.js                 # כניסה, הגדרות, חיבור מודולים
│  ├─ engine.js               # TreeWalker, MutationObserver, תור, בקרת קצב
│  ├─ dictionary.js           # טעינה, נרמול, exact/scoped/template
│  ├─ rtl.js                  # auto / css / isolate
│  ├─ skip.js                 # כללי skip (סלקטורים)
│  ├─ collector.js            # איסוף, סף חזרתיות, נרמול, ייצוא
│  ├─ menu.js                 # פקודות Tampermonkey
│  ├─ storage.js              # GM_getValue / GM_setValue
│  └─ meta.header.js          # בלוק ‎// ==UserScript==
├─ dict/
│  ├─ he.json                 # נוצר בבנייה
│  └─ sources/
│     ├─ manual.json
│     ├─ reviewed.json
│     ├─ seed-app.json
│     └─ pending.json
├─ tools/
│  ├─ build.mjs               # איחוד src + מילון → dist
│  ├─ merge_collected.mjs
│  ├─ validate_dict.mjs
│  ├─ seed_from_app.py
│  └─ extract_from_html.mjs   # (אופציונלי) / extract_from_zim.py
├─ dist/
│  └─ github-he.user.js       # מוכן להתקנה; כתובת העדכון
├─ test/
│  ├─ dictionary.test.js
│  ├─ engine.test.js          # jsdom
│  └─ fixtures/               # קטעי HTML שנשמרו
├─ docs/
│  ├─ SPEC.md                 # המסמך הזה
│  ├─ GLOSSARY.md
│  └─ CONTRIBUTING.md
└─ .github/
   ├─ workflows/ci.yml        # lint + test + validate_dict + build
   └─ ISSUE_TEMPLATE/collected-strings.yml
```

---

## 10. בנייה והפצה

### 10.1 כלים
Node ≥ 18, `esbuild` (חבילת IIFE יחידה), בלי תלויות בזמן ריצה.

### 10.2 כותרת הסקריפט (דוגמה)
```js
// ==UserScript==
// @name         GitHub בעברית
// @namespace    https://github.com/<USER>/github-he
// @version      0.0.1
// @description  תרגום ממשק GitHub לעברית
// @match        https://github.com/*
// @run-at       document-start
// @noframes
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @grant        GM_addStyle
// @grant        GM_download
// @grant        unsafeWindow
// @updateURL    https://raw.githubusercontent.com/<USER>/github-he/main/dist/github-he.user.js
// @downloadURL  https://raw.githubusercontent.com/<USER>/github-he/main/dist/github-he.user.js
// ==/UserScript==
```
אין `@connect` ואין `@require`: אין רשת בזמן ריצה.

### 10.3 הפצה ב-NetFree
- וידוא שהכתובת `raw.githubusercontent.com` נגישה ברשת המסוננת. אם לא: לצרף את קובץ ה-`.user.js` לפוסט בפורום ולהתקין ממנו ידנית.
- הקובץ אמור לעבוד כסקריפט מקומי (נפתח בתוך Tampermonkey) גם בלי עדכון אוטומטי.

### 10.4 גרסאות
SemVer. `0.0.x` = ניסיוני. כל שינוי מילון בלבד = העלאת patch.

### 10.5 אזהרת שימוש וסימן מסחרי
הפרויקט **אינו קשור ל-GitHub** ואינו מאושר על ידה. השם לא מכיל את "GitHub" כשם המוצר אלא כתיאור ("for GitHub"). יש לכלול הערה כזו ב-README. הטקסטים האנגליים הם מפתחות בלבד.

---

## 11. בדיקות וקריטריוני קבלה ל-0.0.1

### 11.1 דפים לבדיקה
1. מסך כניסה (לא מחובר) — `/login`
2. דשבורד — `/`
3. עמוד repo ציבורי — `/:owner/:repo`
4. רשימת issues + issue בודד
5. רשימת pull requests + PR בודד (Conversation ו-Files changed)
6. עמוד Actions
7. התראות — `/notifications`
8. חיפוש — `/search?q=…`
9. הגדרות פרופיל — `/settings/profile`
10. Explore / Trending

### 11.2 קריטריונים
| # | קריטריון | יעד |
|---|---|---|
| 1 | כיסוי מחרוזות ממשק בדפים 1–10 | ≥ 70% (נמדד עם `GHE.stats()`) |
| 2 | אין תרגום של תוכן משתמשים בדפים 3–5 | 0 מקרים בדגימה של 20 דפים |
| 3 | אין שבירת פעולות: יצירת issue, הערה, merge, חיפוש, התחברות | עובר |
| 4 | אין שגיאות בקונסולה שנובעות מהסקריפט | 0 |
| 5 | זמן עיבוד לדף טיפוסי | < 50ms מצטבר ל-MutationObserver בין פריימים |
| 6 | ניווט פנימי (Turbo) ממשיך להיות מתורגם | עובר |
| 7 | כיוון: אייקונים לא זזים, אין יישור לימין | עובר בבדיקה ויזואלית |
| 8 | איסוף: מחרוזת מופיעה בייצוא רק אחרי הסף | עובר |
| 9 | איסוף: לא פעיל בדף repo פרטי | עובר |
| 10 | CI ירוק (lint, validate_dict, test, build) | עובר |

### 11.3 בדיקות יחידה (jsdom)
- נרמול: רווחים, NBSP, שורות חדשות.
- סדר חיפוש: scoped > exact > template.
- תבניות: `{n}`, `{1}`, יחיד/רבים.
- skip: צמתים בתוך `code`, `.markdown-body` וכו' לא משתנים.
- אין לולאת עדכונים בין הסקריפט ל-React (סימולציה).

---

## 12. סיכונים

| סיכון | חומרה | התמודדות |
|---|---|---|
| GitHub משנה את ה-DOM / הטקסטים | בינונית | מילון מבוסס טקסט (לא סלקטורים); עדכונים תכופים דרך האיסוף |
| שבירת React בגלל שינוי צמתים | גבוהה | שינוי `nodeValue` בלבד; בלי החלפת צמתים; בדיקות ידניות |
| טעויות כיוון (RTL) בכפתורים/אייקונים | בינונית | מצב `auto`; אפשרות מעבר ל-`css`/`isolate`/`off` |
| תוכן משתמשים שתורגם בטעות | נמוכה | התאמה מלאה בלבד + skip |
| דליפת פרטיות באיסוף | גבוהה | סעיף 6.4: סף חזרתיות, נרמול, כיבוי ב-repos פרטיים, אחסון מקומי בלבד |
| הבהוב אנגלית לפני החלפה | נמוכה | `document-start` + עיבוד מוקדם |
| ביצועים בדפים גדולים (PR עם אלפי שורות) | בינונית | skip לקוד; עיבוד בקבוצות; תקרת זמן לכל פריים |
| חסימת `raw.githubusercontent.com` ב-NetFree | נמוכה-בינונית | הפצה חלופית בקובץ (סעיף 10.3) |
| דיוק התרגום מהאפליקציה בהקשר של האתר | בינונית | הזרע מסומן "לא מאושר"; מופעל רק בהתאמה מדויקת |

---

## 13. מפת דרכים (לא מחייב)

- **0.1:** תבניות שלד לטקסט מפוצל; תאריכים יחסיים; חלון ביקורת לאיסוף; Greasy Fork / OpenUserJS.
- **0.2:** GitHub Enterprise Server (דומיינים מותאמים); `gist.github.com`.
- **0.3:** מערכת שפות כלליות (`dict/<lang>.json`).
- מאוחר יותר: כלי חילוץ מ-ZIM, בדיקת regressions אוטומטית מול צילומי DOM.

---

## 14. החלטות שהנחתי (לשנות לפי הצורך)

1. **שם:** `github-he` (בכותרת הסקריפט: "GitHub בעברית").
2. **רישיון:** MIT.
3. **RTL ברירת מחדל:** `auto`.
4. **סף איסוף:** 3 הופעות ב-2 תבניות נתיב.
5. **תאריכים יחסיים:** ניסוי בלבד, לא התחייבות.
6. **טון תרגום:** לשון רבים ניטרלית, שמות פעולה.
7. **ללא רשת בזמן ריצה:** כולל ללא `@require` וללא טעינת מילון מרחוק.
8. **אזור פעולה:** רק `https://github.com/*`.
