diff options
| author | Leitwolf11 | 2026-10-03 13:38:38 +0300 |
|---|---|---|
| committer | Leitwolf11 | 2026-10-03 13:38:38 +0300 |
| commit | ed7f6e57d50765728e7d0513aaf956ec667ddc98 (patch) | |
| tree | 2a3500198ec6e5a18699eac9e84b2484a8e51cc0 | |
| parent | 3173a6a2bfe1975542c8c9cc0707071daa55b587 (diff) | |
| download | website-ed7f6e57d50765728e7d0513aaf956ec667ddc98.tar.gz | |
Revise the Hebrew wording and remember only a visitor's language choice
Hebrew
A second pass over the Hebrew text replaced words a Hebrew reader would
misread or that are not used for the concept: "native" was rendered with the
word for "local", "lowering" with the word for "download", "pipeline" with a
literal pipe and "front end" with a word not used for compilers. Typed
forms, diagnostics, the ecosystem and the standard library use the usual
terms. The text is still not reviewed by a native speaker.
Language choice
A language change is now stored only after the visitor has acted on the
page, as reported by navigator.userActivation. A change that reaches the
page before anyone has clicked, tapped or typed still changes the language
but is not remembered, so only a person can make a choice. A browser without
the activation API keeps the earlier behaviour.
During local testing a choice was once found stored without one being made.
It could not be reproduced in three attempts, and restoring form state was
shown not to fire a change event, so the cause is unknown; this guard closes
the path in the page itself.
Verified locally: frontend tests, type check, prettier and build. In a
browser, a scripted change before any interaction switched the language and
stored nothing, and a change after a real click was stored and survived a
navigation. The forum page was opened in a browser: one notice, no language
menu, English for a Turkish browser, and the Hebrew, German and Russian
notices when the script ran with those browser languages.
| -rw-r--r-- | frontend/src/locales.ts | 13 | ||||
| -rw-r--r-- | frontend/src/pages/GettingStartedPage.vue | 8 | ||||
| -rw-r--r-- | frontend/src/pages/HomePage.vue | 6 | ||||
| -rw-r--r-- | frontend/src/pages/OverviewPage.vue | 26 | ||||
| -rw-r--r-- | frontend/src/preferences.ts | 15 | ||||
| -rw-r--r-- | frontend/src/seo.ts | 2 | ||||
| -rw-r--r-- | frontend/tests/locales.test.mjs | 8 | ||||
| -rw-r--r-- | frontend/tests/seo.test.mjs | 2 |
8 files changed, 55 insertions, 25 deletions
diff --git a/frontend/src/locales.ts b/frontend/src/locales.ts index 34e2bba..4d99d09 100644 --- a/frontend/src/locales.ts +++ b/frontend/src/locales.ts @@ -53,6 +53,19 @@ export function initialLocale(chosen: string | null, languages: readonly string[ } /** + * Whether a language change may be remembered as the visitor's choice. + * + * A choice needs a visitor: a change that reaches the page before anyone has + * clicked, tapped or typed in it did not come from the language menu, so it + * changes the page but is not stored. The argument is the browser's + * `navigator.userActivation`; a browser without it cannot tell, and there a + * change counts as a choice. + */ +export function isVisitorChoice(activation: { hasBeenActive: boolean } | undefined): boolean { + return activation?.hasBeenActive ?? true; +} + +/** * The direction in which the text of a language runs. * * Only text follows it. The page layout, the navigation and code samples keep diff --git a/frontend/src/pages/GettingStartedPage.vue b/frontend/src/pages/GettingStartedPage.vue index e6bfd82..ecde15f 100644 --- a/frontend/src/pages/GettingStartedPage.vue +++ b/frontend/src/pages/GettingStartedPage.vue @@ -195,7 +195,7 @@ const text = { "שכפלו את מאגר המהדר יחד עם תת־המודולים שלו. השתמשו במחשב פיתוח נתמך עם Windows 10/11 או עם גרסה עדכנית של macOS.", step2: "2. בדיקת שרשרת הכלים", step2Text: - "פקודת הפיתוח של המאגר, הכתובה ב-Go, בודקת את הדרישות המקומיות. המהדר משתמש ב-GHC2024 לחזית הכתובה ב-Haskell, ב-Clang/LLVM לשלבים המקומיים ב-C++20, ב-Bazel לבניות המקומיות וב-Temurin JDK 25 ל-DSL של הפרויקטים.", + "פקודת הפיתוח של המאגר, הכתובה ב-Go, בודקת את הדרישות המקומיות. המהדר משתמש ב-GHC2024 ל-frontend הכתוב ב-Haskell, ב-Clang/LLVM לשלבי ה-native ב-C++20, ב-Bazel לבניית חלקי ה-native וב-Temurin JDK 25 ל-DSL של הפרויקטים.", platform: "מחשבי הפיתוח הנתמכים הם Windows 10/11 ו-macOS Sequoia/Tahoe. ב-Windows נעשה שימוש ב-clang-cl וב-LLD עצמאיים של LLVM יחד עם Windows SDK וספריות ה-CRT/STL של MSVC. ב-macOS יש להתקין את Xcode Command Line Tools; יישום Xcode המלא אינו נדרש.", toolchainHelp: @@ -203,12 +203,12 @@ const text = { bootstrap: "הכנת מחשב אופציונלית", step3: "3. בנייה ובדיקה", step3Text: - "בנו את המהדר המקומי והריצו את חבילות בדיקות החוזה המקומיות שלו. פקודת ה-Go מתזמרת את הבדיקות; Bazel נשאר הבעלים של גרף הבנייה של C++20. Cabal ו-Gradle מטפלים בגבולות של Haskell ושל Kotlin.", + "בנו את מהדר ה-native והריצו את חבילות בדיקות החוזה שלו. פקודת ה-Go מתזמרת את הבדיקות; Bazel נשאר הבעלים של גרף הבנייה של C++20. Cabal ו-Gradle מטפלים בגבולות של Haskell ושל Kotlin.", step4: "4. קריאה ובדיקה של תוכנית אמיתית", step4Text: "בדוגמה HelloWorld נקודת הכניסה היא מתודה של מחלקה. Visual X# אינה משתמשת בפונקציה ברמה העליונה כנקודת הכניסה לתוכנית. בדקו את קובץ המקור המדויק, ואז בנו אותו בתת־הקבוצה הנוכחית של המהדר.", fileCheck: "בדיקת קובץ מקור יחיד", - fileBuild: "בניית קובץ הרצה מקומי", + fileBuild: "בניית קובץ הרצה בקוד מכונה", subset: "תת־הקבוצה של קוד המקור שמתקבלת צרה מהמפרט הציבורי המלא. בנייה מוצלחת היא ראיה חזקה יותר לתוכנית מסוימת מאשר דוגמת תחביר במפרט; הרצת בדיקות מוצלחת היא ראיה חזקה יותר למשפחת יכולות.", step5: "5. לעקוב אחר החוזה הציבורי", @@ -222,7 +222,7 @@ const text = { }, { title: "מדריך בנייה", - detail: "מחשבים רשמיים, איתור שרשרת הכלים ויעדי בנייה מקומיים.", + detail: "מחשבים רשמיים, איתור שרשרת הכלים ויעדי הבנייה של חלקי ה-native.", href: "https://github.com/Progmasoft/visual-xsharp/blob/main/Documents/BUILDING.md", }, { diff --git a/frontend/src/pages/HomePage.vue b/frontend/src/pages/HomePage.vue index fbdff2f..f432632 100644 --- a/frontend/src/pages/HomePage.vue +++ b/frontend/src/pages/HomePage.vue @@ -197,7 +197,7 @@ const translations = { eyebrow: "שפה לבנייה מתוך בהירות", title: "להפוך את התוכנית כולה למובנת.", intro: - "Visual X# שואפת לשלב קוד מקור רב־הבעה עם בעלות מפורשת, צינור מהדר גלוי ופלט מקומי. היא נבנית בפתיחות: המפרט מתאר את השפה המיועדת, וכיסוי המימוש עדיין הולך וגדל.", + "Visual X# שואפת לשלב קוד מקור אקספרסיבי עם בעלות מפורשת, תהליך הידור גלוי ופלט בקוד מכונה. היא נבנית בפתיחות: המפרט מתאר את השפה המיועדת, וכיסוי המימוש עדיין הולך וגדל.", start: "להתחיל לקרוא", overview: "למה Visual X#?", github: "למעקב אחר הפיתוח", @@ -218,7 +218,7 @@ const translations = { { number: "03", title: "מהדר שאפשר לבחון", - text: "לניתוח התחבירי, לפענוח השמות, לבדיקת הטיפוסים, ל-Core, ל-Xpp ול-Xmm יש תחומי אחריות נפרדים. הדרך אל LLVM ואל קובץ הרצה מקומי גלויה ואינה קופסה שחורה.", + text: "לניתוח התחבירי, לפענוח השמות, לבדיקת הטיפוסים, ל-Core, ל-Xpp ול-Xmm יש תחומי אחריות נפרדים. הדרך אל LLVM ואל קובץ הרצה בקוד מכונה גלויה ואינה קופסה שחורה.", }, ], codeTitle: "נקודת התחלה מוכרת", @@ -242,7 +242,7 @@ const translations = { }, { title: "לעקוב אחר העבודה", - body: "המאגר מכיל את השפה, את שלבי המהדר המקומיים, את כלי הפרויקט, דוגמאות ומסמכי פיתוח. האתר הזה הוא מדריך אל העבודה הזאת, לא הצהרה על שרשרת כלים מוגמרת.", + body: "המאגר מכיל את השפה, את שלבי ה-native של המהדר, את כלי הפרויקט, דוגמאות ומסמכי פיתוח. האתר הזה הוא מדריך אל העבודה הזאת, לא הצהרה על שרשרת כלים מוגמרת.", link: "למאגר", href: "https://github.com/Progmasoft/visual-xsharp", }, diff --git a/frontend/src/pages/OverviewPage.vue b/frontend/src/pages/OverviewPage.vue index d259789..af40b98 100644 --- a/frontend/src/pages/OverviewPage.vue +++ b/frontend/src/pages/OverviewPage.vue @@ -357,7 +357,7 @@ const text = { navStart: "צעדים ראשונים", section: "תיעוד / סקירה", title: "מה Visual X# מנסה לפתור", - lead: "Visual X# מתוכננת סביב שאלה פשוטה: האם קוד רב־הבעה יכול להישאר מובן כאשר בעלות, טיפוסים והידור מקומי חשובים? העמוד הזה מסביר את הכיוון, ואינו הבטחה שכל יכולת כבר מוגמרת.", + lead: "Visual X# מתוכננת סביב שאלה פשוטה: האם קוד אקספרסיבי יכול להישאר מובן כאשר בעלות, טיפוסים והידור לקוד מכונה חשובים? העמוד הזה מסביר את הכיוון, ואינו הבטחה שכל יכולת כבר מוגמרת.", goals: "יעדי התכנון", modelHeading: "כמה בחירות של השפה", modelIntro: @@ -372,12 +372,12 @@ const text = { body: "טיפוס ערך נשאר טיפוס ערך רק כאשר כל ארגומנט גנרי מקונן הוא טיפוס ערך. טיפוס הפניה בכל מקום במבנה הגנרי הופך את התוצאה לטיפוס הפניה.", }, { - title: "משטח אחד של ספרייה סטנדרטית", + title: "ספרייה סטנדרטית אחת", body: "מרחבי השמות של System מחזיקים את היכולות הבסיסיות, כגון קלט ופלט במסוף, מערכים, טקסט ומתמטיקה. מרחב שמות של הספרייה הסטנדרטית אינו תלות חבילה שמצהירים עליה בנפרד.", }, { - title: "פלט מקומי, בלי להעמיד פנים שהצינור מוגמר", - body: "למהדר יש נתיב מקוד מקור לקוד מקומי עבור תת־הקבוצה שמומשה. היעד הוא כיסוי סמנטי רחב יותר, גבולות ביניים מאומתים ואבחונים אמינים ככל שהשפה גדלה.", + title: "פלט בקוד מכונה, בלי להעמיד פנים שהתהליך מוגמר", + body: "למהדר יש נתיב מקוד מקור לקוד מכונה עבור תת־הקבוצה שמומשה. היעד הוא כיסוי סמנטי רחב יותר, גבולות ביניים מאומתים והודעות אבחון אמינות ככל שהשפה גדלה.", }, ], pipelineHeading: "למהדר יש גבולות גלויים", @@ -386,23 +386,23 @@ const text = { pipeline: [ { title: "קוד מקור ותחביר", - body: "המנתח הלקסיקלי והמנתח התחבירי, הכתובים ב-Haskell, מפיקים AST מנותח ושומרים את מיקומי המקור לצורך אבחונים.", + body: "המנתח הלקסיקלי והמנתח התחבירי, הכתובים ב-Haskell, מפיקים AST מנותח ושומרים את מיקומי המקור לצורך הודעות אבחון.", }, { title: "שמות וטיפוסים", - body: "שינוי שמות, פענוח שמות ובדיקת טיפוסים מפיקים צורות AST נפרדות: מפוענחת ומוגדרת טיפוסים.", + body: "שינוי שמות, פענוח שמות ובדיקת טיפוסים מפיקים צורות AST נפרדות: מפוענחת ובעלת טיפוסים.", }, { title: "סמנטיקת השפה", body: "פירוק הסוכר התחבירי והאופטימיזציות של Core פועלים לפני המתאם הפרטי CorePrep.", }, { - title: "התאמה מקומית", + title: "התאמה לקוד מכונה", body: "השלבים Xpp ו-Xmm, הכתובים ב-C++20, נושאים ייצוג מאומת ובלתי תלוי ביעד לקראת LLVM.", }, { title: "קובץ הרצה", - body: "LLVM מוריד את התוכנית המאומתת לקוד אובייקט, ולאחר מכן שלב קישור של הפלטפורמה יוצר תוצר .vxse.", + body: "LLVM מתרגם את התוכנית המאומתת לקוד אובייקט, ולאחר מכן שלב קישור של הפלטפורמה יוצר תוצר .vxse.", }, ], boundariesHeading: "היכן עוברים הגבולות היום", @@ -413,7 +413,7 @@ const text = { }, { label: "האמת של המימוש", - value: "בדיקות המהדר, תת־הקבוצה הנתמכת ואבחונים מפורשים", + value: "בדיקות המהדר, תת־הקבוצה הנתמכת והודעות אבחון מפורשות", }, { label: "הגדרת הפרויקט", @@ -446,15 +446,15 @@ const text = { items: [ { title: "סמנטיקה לפני קיצורי דרך", - body: "לשפת המקור יש מפרט כתוב. המהדר מפריד בין תחביר מנותח, מפוענח ומוגדר טיפוסים, כך שהמעברים המאוחרים מקבלים משמעות מפורשת במקום לנחש.", + body: "לשפת המקור יש מפרט כתוב. המהדר מפריד בין תחביר מנותח, מפוענח ובעל טיפוסים, כך שהמעברים המאוחרים מקבלים משמעות מפורשת במקום לנחש.", }, { title: "בעלות שאפשר לעקוב אחריה", - body: "ההתנהגות של הפניות ושל ערכים מוגדרת דרך הרכב הטיפוסים. ספירת הפניות אוטומטית וכללי העתקה־בעת־כתיבה הם חלק ממודל השפה; התמיכה בזמן ריצה ובהורדה עדיין בהשלמה.", + body: "ההתנהגות של הפניות ושל ערכים מוגדרת דרך הרכב הטיפוסים. ספירת הפניות אוטומטית וכללי העתקה־בעת־כתיבה הם חלק ממודל השפה; התמיכה בזמן ריצה ובתרגום לשלבים הנמוכים עדיין בהשלמה.", }, { - title: "צינור מקומי מכוון", - body: "החזית מורידה את התוכנית דרך Core ו-CorePrep, ולאחר מכן Xpp ו-Xmm מתאימים אותה ליצירת הקוד של LLVM. הגבולות מאפשרים אימות ואבחונים בכל שלב.", + title: "תהליך הידור מכוון לקוד מכונה", + body: "ה-frontend מתרגם את התוכנית דרך Core ו-CorePrep, ולאחר מכן Xpp ו-Xmm מתאימים אותה ליצירת הקוד של LLVM. הגבולות מאפשרים אימות והודעות אבחון בכל שלב.", }, ], not: "מה היא איננה", diff --git a/frontend/src/preferences.ts b/frontend/src/preferences.ts index 18d6615..a809bab 100644 --- a/frontend/src/preferences.ts +++ b/frontend/src/preferences.ts @@ -2,7 +2,13 @@ // SPDX-License-Identifier: AGPL-3.0-or-later WITH AdditionRef-Progmasoft-Patent-Grant-1.1 import { computed, ref, watch } from "vue"; -import { forwardArrow, initialLocale, textDirection, type Locale } from "./locales"; +import { + forwardArrow, + initialLocale, + isVisitorChoice, + textDirection, + type Locale, +} from "./locales"; export { supportedLocales, type Locale } from "./locales"; export type Theme = "dark" | "light"; @@ -30,10 +36,13 @@ function showLocale(value: Locale): void { } showLocale(locale.value); -// The language changes only through the menu, so a change is a choice. +// The language changes through the menu. It is remembered only when a +// visitor has acted on the page, so nothing but a person makes a choice. watch(locale, (value) => { showLocale(value); - localStorage.setItem(localeChoiceKey, value); + if (isVisitorChoice(navigator.userActivation)) { + localStorage.setItem(localeChoiceKey, value); + } }); watch( diff --git a/frontend/src/seo.ts b/frontend/src/seo.ts index c4e5e82..0df93f0 100644 --- a/frontend/src/seo.ts +++ b/frontend/src/seo.ts @@ -66,7 +66,7 @@ const metadata: Record<Locale, Record<PublicRoute, PageMetadata>> = { "/": { title: "Visual X# — להפוך את התוכנית כולה למובנת", description: - "הכירו את Visual X#: שפת תכנות מקומית הבנויה סביב קוד מקור קריא, בעלות מפורשת וצינור מהדר שאפשר לבחון.", + "הכירו את Visual X#: שפת תכנות שמהודרת לקוד מכונה והבנויה סביב קוד מקור קריא, בעלות מפורשת ותהליך הידור שאפשר לבחון.", }, "/docs/overview": { title: "סקירה · Visual X#", diff --git a/frontend/tests/locales.test.mjs b/frontend/tests/locales.test.mjs index 64509a5..0b87174 100644 --- a/frontend/tests/locales.test.mjs +++ b/frontend/tests/locales.test.mjs @@ -9,6 +9,7 @@ import { forwardArrow, initialLocale, isLocale, + isVisitorChoice, localeLabels, supportedLocales, textDirection, @@ -64,3 +65,10 @@ test("only Hebrew text runs right to left", () => { assert.equal(forwardArrow(locale), expected === "rtl" ? "←" : "→", locale); } }); + +test("a language change is remembered only after the visitor acted on the page", () => { + assert.equal(isVisitorChoice({ hasBeenActive: true }), true); + assert.equal(isVisitorChoice({ hasBeenActive: false }), false); + // A browser that does not report activation cannot tell; the change counts. + assert.equal(isVisitorChoice(undefined), true); +}); diff --git a/frontend/tests/seo.test.mjs b/frontend/tests/seo.test.mjs index 7104f5c..82de8f9 100644 --- a/frontend/tests/seo.test.mjs +++ b/frontend/tests/seo.test.mjs @@ -96,7 +96,7 @@ test("the Hebrew routes have their own titles and keep the canonical URLs", () = const { document, elements } = fakeDocument(); applyPageMetadata("/", "he-IL"); assert.match(document.title, /^Visual X# — להפוך את התוכנית כולה למובנת$/); - assert.match(elements.get('meta[name="description"]').content, /שפת תכנות מקומית/); + assert.match(elements.get('meta[name="description"]').content, /שמהודרת לקוד מכונה/); assert.equal(elements.get('link[rel="canonical"]').href, "https://xsharp-lang.xyz/"); applyPageMetadata("/docs/overview", "he-IL"); assert.equal(document.title, "סקירה · Visual X#"); |