diff options
| author | Leitwolf11 | 2026-10-03 13:00:49 +0300 |
|---|---|---|
| committer | Leitwolf11 | 2026-10-03 13:00:49 +0300 |
| commit | 3173a6a2bfe1975542c8c9cc0707071daa55b587 (patch) | |
| tree | a4883bcc56b81beaaa339fabd0099251acdde643 /frontend/src | |
| parent | 3bb41fe5c59dcf25edfd868e0d6eca67e4a6e504 (diff) | |
| download | website-3173a6a2bfe1975542c8c9cc0707071daa55b587.tar.gz | |
Add Hebrew, follow the browser language, and gate the backend docs
Language
Hebrew (he-IL) joins English, German and Russian on the language site, in
the page metadata and in the list the backend reports.
A visitor who has not chosen a language now starts in the language of their
browser: the first preferred language that is published, compared by primary
language, with `iw` accepted as the former code for Hebrew. A browser that
prefers no published language gets en-US. A choice made in the menu is kept
under a new storage key and always wins. The earlier key was written on every
visit, so it could not tell a choice from the former English default; it is
no longer read, which resets an earlier explicit choice once.
Hebrew does not mirror the page. The layout, the navigation and code keep
their left-to-right arrangement; only text follows the language. The onward
arrow points left in Hebrew, and a left-to-right mark keeps "Visual X#"
intact inside right-to-left text.
The forum placeholder no longer has a language menu. It shows its one notice
in the language of the browser, with English as the fallback, and stores no
language. Its assets get a new cache key.
The single documentation column may now shrink below its widest code line.
Before, a long command widened the page on narrow screens, which cut
right-aligned text off.
The Hebrew text was not reviewed by a native speaker.
Documentation gate
The backend applies Dokka 2.2.0 with reportUndocumented and failOnWarning,
its public declarations have KDoc, and CI runs dokkaGenerate. The dependency
lock file and the verification metadata include the new artifacts.
Verified locally: frontend tests, type check, prettier and build; backend
test, bootJar and dokkaGenerate, which was also shown to fail on an
undocumented declaration; the Hebrew pages in a browser. The forum page was
exercised by its tests only.
Diffstat (limited to 'frontend/src')
| -rw-r--r-- | frontend/src/App.vue | 20 | ||||
| -rw-r--r-- | frontend/src/locales.ts | 68 | ||||
| -rw-r--r-- | frontend/src/pages/GettingStartedPage.vue | 64 | ||||
| -rw-r--r-- | frontend/src/pages/HomePage.vue | 70 | ||||
| -rw-r--r-- | frontend/src/pages/OverviewPage.vue | 118 | ||||
| -rw-r--r-- | frontend/src/preferences.ts | 40 | ||||
| -rw-r--r-- | frontend/src/seo.ts | 19 | ||||
| -rw-r--r-- | frontend/src/styles/site.css | 22 |
8 files changed, 391 insertions, 30 deletions
diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 1aaa109..56b38f5 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -4,7 +4,8 @@ <script setup lang="ts"> import { computed } from "vue"; import { RouterLink, RouterView } from "vue-router"; -import { locale, theme } from "./preferences"; +import { localeLabels } from "./locales"; +import { locale, supportedLocales, theme } from "./preferences"; const labels = { "en-US": { @@ -40,6 +41,17 @@ const labels = { status: "В активной разработке", footer: "Проект Progmasoft. Visual X# — самостоятельный язык со своей идентичностью.", }, + "he-IL": { + home: "בית", + overview: "סקירה", + start: "צעדים ראשונים", + forum: "פורום", + source: "קוד מקור", + theme: "החלפת ערכת הצבעים", + language: "שפה", + status: "בפיתוח פעיל", + footer: "פרויקט של Progmasoft. ל-Visual X# זהות שפה עצמאית.", + }, } as const; const copy = computed(() => labels[locale.value]); @@ -66,9 +78,9 @@ function toggleTheme() { <label class="language-control"> <span class="sr-only">{{ copy.language }}</span> <select v-model="locale" :aria-label="copy.language"> - <option value="en-US">EN</option> - <option value="de-DE">DE</option> - <option value="ru-RU">RU</option> + <option v-for="supported in supportedLocales" :key="supported" :value="supported"> + {{ localeLabels[supported] }} + </option> </select> </label> <button class="icon-button" type="button" :aria-label="copy.theme" @click="toggleTheme"> diff --git a/frontend/src/locales.ts b/frontend/src/locales.ts new file mode 100644 index 0000000..34e2bba --- /dev/null +++ b/frontend/src/locales.ts @@ -0,0 +1,68 @@ +// SPDX-FileCopyrightText: 2026 Progmasoft <support@progmasoft.com> +// SPDX-License-Identifier: AGPL-3.0-or-later WITH AdditionRef-Progmasoft-Patent-Grant-1.1 + +export const supportedLocales = ["en-US", "de-DE", "ru-RU", "he-IL"] as const; +export type Locale = (typeof supportedLocales)[number]; + +/** The language of a visitor whose browser asks for none that is published. */ +export const fallbackLocale: Locale = "en-US"; + +/** The short label of each language in the language menu. */ +export const localeLabels: Record<Locale, string> = { + "en-US": "EN", + "de-DE": "DE", + "ru-RU": "RU", + "he-IL": "HE", +}; + +// Primary language subtags and the published locale each one selects. `iw` is +// the former code for Hebrew, which some browsers still report. +const localeByLanguage = new Map<string, Locale>([ + ["en", "en-US"], + ["de", "de-DE"], + ["ru", "ru-RU"], + ["he", "he-IL"], + ["iw", "he-IL"], +]); + +export function isLocale(value: unknown): value is Locale { + return supportedLocales.some((supported) => supported === value); +} + +/** + * The first published locale among the languages a browser prefers. + * + * The argument is the list a browser reports, most preferred first. Only the + * primary language of each entry is compared, so `de-AT` selects German. + */ +export function browserLocale(languages: readonly string[]): Locale { + for (const language of languages) { + const primary = String(language).split("-", 1)[0] ?? ""; + const locale = localeByLanguage.get(primary.toLowerCase()); + if (locale) return locale; + } + return fallbackLocale; +} + +/** + * The language a page starts in: the language the visitor chose, and without + * a choice the language of the browser. + */ +export function initialLocale(chosen: string | null, languages: readonly string[]): Locale { + return isLocale(chosen) ? chosen : browserLocale(languages); +} + +/** + * The direction in which the text of a language runs. + * + * Only text follows it. The page layout, the navigation and code samples keep + * their left-to-right arrangement in every language. + */ +export function textDirection(locale: Locale): "ltr" | "rtl" { + return locale === "he-IL" ? "rtl" : "ltr"; +} + +/** The arrow that points onward in the reading direction of a language. */ +export function forwardArrow(locale: Locale): string { + return textDirection(locale) === "rtl" ? "←" : "→"; +} diff --git a/frontend/src/pages/GettingStartedPage.vue b/frontend/src/pages/GettingStartedPage.vue index abbd8d9..e6bfd82 100644 --- a/frontend/src/pages/GettingStartedPage.vue +++ b/frontend/src/pages/GettingStartedPage.vue @@ -4,7 +4,7 @@ <script setup lang="ts"> import { computed } from "vue"; import { RouterLink } from "vue-router"; -import { locale } from "../preferences"; +import { arrow, locale } from "../preferences"; const text = { "en-US": { @@ -184,6 +184,65 @@ const text = { spec: "Публичная спецификация", examples: "Больше примеров", }, + "he-IL": { + navOverview: "סקירה", + navStart: "צעדים ראשונים", + section: "תיעוד / צעדים ראשונים", + title: "מתחילים מקוד המקור", + lead: "Visual X# נמצאת בפיתוח פעיל. לעת עתה המאגר הוא נקודת הכניסה האמינה לבניית המהדר ולבדיקה אילו יכולות של השפה פועלות מקצה לקצה.", + step1: "1. השגת המאגר", + step1Text: + "שכפלו את מאגר המהדר יחד עם תת־המודולים שלו. השתמשו במחשב פיתוח נתמך עם Windows 10/11 או עם גרסה עדכנית של macOS.", + step2: "2. בדיקת שרשרת הכלים", + step2Text: + "פקודת הפיתוח של המאגר, הכתובה ב-Go, בודקת את הדרישות המקומיות. המהדר משתמש ב-GHC2024 לחזית הכתובה ב-Haskell, ב-Clang/LLVM לשלבים המקומיים ב-C++20, ב-Bazel לבניות המקומיות וב-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: + "אם הבדיקה מדווחת על כלים חסרים, ה-bootstrap האופציונלי יכול להתקין את הדרישות דרך מנהל החבילות של המערכת. לאחר מכן פתחו מסוף חדש כדי ששינויי PATH ייכנסו לתוקף. LLVM_ROOT יכול להצביע על תחילית ההתקנה של חבילת הפיתוח של LLVM; אין לכתוב נתיב ייחודי למחשב בקובצי הפרויקט.", + bootstrap: "הכנת מחשב אופציונלית", + step3: "3. בנייה ובדיקה", + step3Text: + "בנו את המהדר המקומי והריצו את חבילות בדיקות החוזה המקומיות שלו. פקודת ה-Go מתזמרת את הבדיקות; Bazel נשאר הבעלים של גרף הבנייה של C++20. Cabal ו-Gradle מטפלים בגבולות של Haskell ושל Kotlin.", + step4: "4. קריאה ובדיקה של תוכנית אמיתית", + step4Text: + "בדוגמה HelloWorld נקודת הכניסה היא מתודה של מחלקה. Visual X# אינה משתמשת בפונקציה ברמה העליונה כנקודת הכניסה לתוכנית. בדקו את קובץ המקור המדויק, ואז בנו אותו בתת־הקבוצה הנוכחית של המהדר.", + fileCheck: "בדיקת קובץ מקור יחיד", + fileBuild: "בניית קובץ הרצה מקומי", + subset: + "תת־הקבוצה של קוד המקור שמתקבלת צרה מהמפרט הציבורי המלא. בנייה מוצלחת היא ראיה חזקה יותר לתוכנית מסוימת מאשר דוגמת תחביר במפרט; הרצת בדיקות מוצלחת היא ראיה חזקה יותר למשפחת יכולות.", + step5: "5. לעקוב אחר החוזה הציבורי", + step5Text: + "המפרט הציבורי מתאר את הסמנטיקה המיועדת של השפה, ומסמכי המאגר מפרטים את המימוש ואת הכלים. הפלט של -Help במהדר מתאר את הדגלים שהקובץ המותקן מקבל.", + links: [ + { + title: "מפרט השפה", + detail: "חוזי תחביר וסמנטיקה, כולל דוגמאות תקינות ושגויות.", + href: "https://github.com/Progmasoft/visual-xsharp/tree/main/Spec", + }, + { + title: "מדריך בנייה", + detail: "מחשבים רשמיים, איתור שרשרת הכלים ויעדי בנייה מקומיים.", + href: "https://github.com/Progmasoft/visual-xsharp/blob/main/Documents/BUILDING.md", + }, + { + title: "מדריך ה-CLI", + detail: "הפקודות, הדגלים, התנהגות התוצרים ומצב המימוש הנוכחיים.", + href: "https://github.com/Progmasoft/visual-xsharp/blob/main/Documents/CLI.md", + }, + { + title: "תוכניות לדוגמה", + detail: "דוגמאות קוד לצד מקבילות ב-C# 13, ב-C++20 וב-Java 21.", + href: "https://github.com/Progmasoft/visual-xsharp/tree/main/Examples", + }, + ], + note: "מצב המימוש", + noteText: + "דוגמה במפרט אינה הוכחה לתמיכה במהדר. לפני שמסתמכים על יכולת, הריצו את בדיקות המאגר ועיינו בהערות המימוש העדכניות. הורדות בינאריות ותהליך התקנה יציב עדיין אינם מובטחים.", + more: "להבין את התכנון", + spec: "המפרט הציבורי", + examples: "דוגמאות נוספות", + }, } as const; const c = computed(() => text[locale.value]); const hello = `namespace Examples.HelloWorld; @@ -248,7 +307,8 @@ vxs build -Help</code></pre> <p>{{ c.noteText }}</p> </div> <div class="doc-actions"> - <RouterLink class="button button-primary" to="/docs/overview">{{ c.more }} →</RouterLink + <RouterLink class="button button-primary" to="/docs/overview" + >{{ c.more }} {{ arrow }}</RouterLink ><a class="inline-link" href="https://github.com/Progmasoft/visual-xsharp/tree/main/Spec" >{{ c.spec }} ↗</a ><a diff --git a/frontend/src/pages/HomePage.vue b/frontend/src/pages/HomePage.vue index 9b0d617..fbdff2f 100644 --- a/frontend/src/pages/HomePage.vue +++ b/frontend/src/pages/HomePage.vue @@ -4,7 +4,7 @@ <script setup lang="ts"> import { computed } from "vue"; import { RouterLink } from "vue-router"; -import { locale } from "../preferences"; +import { arrow, locale } from "../preferences"; const translations = { "en-US": { @@ -193,6 +193,66 @@ const translations = { disclaimer: "Статус: язык и компилятор развиваются. Примеры в спецификации могут описывать задуманное поведение раньше его полной поддержки компилятором.", }, + "he-IL": { + eyebrow: "שפה לבנייה מתוך בהירות", + title: "להפוך את התוכנית כולה למובנת.", + intro: + "Visual X# שואפת לשלב קוד מקור רב־הבעה עם בעלות מפורשת, צינור מהדר גלוי ופלט מקומי. היא נבנית בפתיחות: המפרט מתאר את השפה המיועדת, וכיסוי המימוש עדיין הולך וגדל.", + start: "להתחיל לקרוא", + overview: "למה Visual X#?", + github: "למעקב אחר הפיתוח", + stage: "השפה והמהדר בפיתוח פעיל", + why: "למה Visual X#?", + whyIntro: "כלים טובים צריכים לאפשר לך להבין מה הקוד שלך אומר, מה בבעלותו ומה המהדר יעשה בהמשך.", + features: [ + { + number: "01", + title: "קוד קריא, משמעות מפורשת", + text: "מפרט השפה מפריד בין תחביר לכללים סמנטיים, עם דוגמאות שמסמנות צורות תקינות ושגויות. פחות הנחות נסתרות מקלות על דיון בבסיסי קוד גדולים.", + }, + { + number: "02", + title: "בעלות היא עניין של השפה", + text: "יש הבחנה בין טיפוסי הפניה, טיפוסי ערך וטיפוסי העתקה־בעת־כתיבה; AARC ופעולות ביניים מפורשות הם חלק מתכנון המהדר. זהו כיוון שנמצא במימוש, לא הצהרה על תמיכה מוגמרת בזמן ריצה.", + }, + { + number: "03", + title: "מהדר שאפשר לבחון", + text: "לניתוח התחבירי, לפענוח השמות, לבדיקת הטיפוסים, ל-Core, ל-Xpp ול-Xmm יש תחומי אחריות נפרדים. הדרך אל LLVM ואל קובץ הרצה מקומי גלויה ואינה קופסה שחורה.", + }, + ], + codeTitle: "נקודת התחלה מוכרת", + codeCaption: "התוכנית הראשונה רגילה בכוונה. עומק התכנון מתגלה ככל שהתוכניות גדלות.", + statusLabel: "המצב הנוכחי", + statusTitle: "תכנון שאפשר לבחון. מהדר שעדיין גדל.", + statusLead: + "למפרט ולמימוש תפקידים שונים. אנחנו מציגים את שניהם כדי שאפשר יהיה להבחין בין כלל מתוכנן לבין יכולת שאפשר להריץ כבר היום.", + statusItems: [ + { + title: "לקרוא את החוזה", + body: "המפרט הציבורי מתעד את הדקדוק והסמנטיקה של השפה, כולל דוגמאות שמבחינות בין צורות תקינות לשגויות. הוא יכול לתאר התנהגות לפני שהמהדר תומך בה.", + link: "לפתיחת המפרט", + href: "https://github.com/Progmasoft/visual-xsharp/tree/main/Spec", + }, + { + title: "לבדוק את המימוש", + body: "המהדר הנוכחי, בדיקות הרכיבים שלו ומדריך ה-CLI מראים אילו נתיבי קוד מקור ואילו תוצרים עובדים כעת. לפני שמסתמכים על יכולת חדשה, כדאי לבדוק אותה בתוכנית קטנה.", + link: "לקריאת מדריך ה-CLI", + href: "https://github.com/Progmasoft/visual-xsharp/blob/main/Documents/CLI.md", + }, + { + title: "לעקוב אחר העבודה", + body: "המאגר מכיל את השפה, את שלבי המהדר המקומיים, את כלי הפרויקט, דוגמאות ומסמכי פיתוח. האתר הזה הוא מדריך אל העבודה הזאת, לא הצהרה על שרשרת כלים מוגמרת.", + link: "למאגר", + href: "https://github.com/Progmasoft/visual-xsharp", + }, + ], + journey: "להכיר את השפה", + journeyText: + "קראו את הסקירה כדי להבין את התכנון, ואז המשיכו למדריך הצעדים הראשונים עם שרשרת הכלים הנוכחית. עבודה שטרם הושלמה מסומנת ביושר.", + disclaimer: + "מצב: שפה ומהדר מתפתחים. דוגמאות במפרט עשויות לתאר התנהגות מתוכננת לפני שקיימת לה תמיכה מלאה במהדר.", + }, } as const; const c = computed(() => translations[locale.value]); const example = `namespace Examples.HelloWorld; @@ -214,7 +274,7 @@ public class HelloWorld { <p class="hero-intro">{{ c.intro }}</p> <div class="hero-actions"> <RouterLink class="button button-primary" to="/docs/getting-started" - >{{ c.start }} <span aria-hidden="true">→</span></RouterLink + >{{ c.start }} <span aria-hidden="true">{{ arrow }}</span></RouterLink ><RouterLink class="button button-quiet" to="/docs/overview">{{ c.overview }}</RouterLink> </div> <p class="release-note">{{ c.stage }}</p> @@ -239,7 +299,7 @@ public class HelloWorld { <span class="kicker">02 / SOURCE</span> <h2 id="example-heading">{{ c.codeTitle }}</h2> <p>{{ c.codeCaption }}</p> - <RouterLink class="inline-link" to="/docs/overview">{{ c.overview }} →</RouterLink> + <RouterLink class="inline-link" to="/docs/overview">{{ c.overview }} {{ arrow }}</RouterLink> </div> <div class="code-card"> <div class="code-bar"><span>HelloWorld.vxs</span></div> @@ -268,7 +328,9 @@ public class HelloWorld { <h2>{{ c.journey }}</h2> <p>{{ c.journeyText }}</p> </div> - <RouterLink class="button button-primary" to="/docs/overview">{{ c.overview }} →</RouterLink> + <RouterLink class="button button-primary" to="/docs/overview" + >{{ c.overview }} {{ arrow }}</RouterLink + > </section> <p class="status-disclaimer section-wrap">{{ c.disclaimer }}</p> </template> diff --git a/frontend/src/pages/OverviewPage.vue b/frontend/src/pages/OverviewPage.vue index b61347a..d259789 100644 --- a/frontend/src/pages/OverviewPage.vue +++ b/frontend/src/pages/OverviewPage.vue @@ -4,7 +4,7 @@ <script setup lang="ts"> import { computed } from "vue"; import { RouterLink } from "vue-router"; -import { locale } from "../preferences"; +import { arrow, locale } from "../preferences"; const text = { "en-US": { @@ -352,6 +352,120 @@ const text = { spec: "Читать спецификацию", source: "Смотреть исходный код", }, + "he-IL": { + navOverview: "סקירה", + navStart: "צעדים ראשונים", + section: "תיעוד / סקירה", + title: "מה Visual X# מנסה לפתור", + lead: "Visual X# מתוכננת סביב שאלה פשוטה: האם קוד רב־הבעה יכול להישאר מובן כאשר בעלות, טיפוסים והידור מקומי חשובים? העמוד הזה מסביר את הכיוון, ואינו הבטחה שכל יכולת כבר מוגמרת.", + goals: "יעדי התכנון", + modelHeading: "כמה בחירות של השפה", + modelIntro: + "אלה חוזי תכנון שמתוארים במפרט הציבורי. לפני שמתייחסים לחוזה כאל יכולת שמישה בסביבת ייצור, יש לבדוק את הכיסוי הנוכחי של המהדר.", + model: [ + { + title: "נקודת הכניסה שייכת למחלקה", + body: "תוכנית מתחילה במתודה public static void Main() ללא פרמטרים במחלקת הכניסה שהוגדרה. המחלקה יכולה להימצא בכל קובץ מקור; מרחב השמות שלה אינו חייב לשקף את מבנה התיקיות.", + }, + { + title: "לטיפוסים יש השלכות על בעלות", + body: "טיפוס ערך נשאר טיפוס ערך רק כאשר כל ארגומנט גנרי מקונן הוא טיפוס ערך. טיפוס הפניה בכל מקום במבנה הגנרי הופך את התוצאה לטיפוס הפניה.", + }, + { + title: "משטח אחד של ספרייה סטנדרטית", + body: "מרחבי השמות של System מחזיקים את היכולות הבסיסיות, כגון קלט ופלט במסוף, מערכים, טקסט ומתמטיקה. מרחב שמות של הספרייה הסטנדרטית אינו תלות חבילה שמצהירים עליה בנפרד.", + }, + { + title: "פלט מקומי, בלי להעמיד פנים שהצינור מוגמר", + body: "למהדר יש נתיב מקוד מקור לקוד מקומי עבור תת־הקבוצה שמומשה. היעד הוא כיסוי סמנטי רחב יותר, גבולות ביניים מאומתים ואבחונים אמינים ככל שהשפה גדלה.", + }, + ], + pipelineHeading: "למהדר יש גבולות גלויים", + pipelineIntro: + "הארכיטקטורה מחולקת בכוונה, כך שכל שלב יכול לדחות קלט פגום ולשמר את מה שהשלב הקודם קבע.", + pipeline: [ + { + title: "קוד מקור ותחביר", + body: "המנתח הלקסיקלי והמנתח התחבירי, הכתובים ב-Haskell, מפיקים AST מנותח ושומרים את מיקומי המקור לצורך אבחונים.", + }, + { + title: "שמות וטיפוסים", + body: "שינוי שמות, פענוח שמות ובדיקת טיפוסים מפיקים צורות AST נפרדות: מפוענחת ומוגדרת טיפוסים.", + }, + { + title: "סמנטיקת השפה", + body: "פירוק הסוכר התחבירי והאופטימיזציות של Core פועלים לפני המתאם הפרטי CorePrep.", + }, + { + title: "התאמה מקומית", + body: "השלבים Xpp ו-Xmm, הכתובים ב-C++20, נושאים ייצוג מאומת ובלתי תלוי ביעד לקראת LLVM.", + }, + { + title: "קובץ הרצה", + body: "LLVM מוריד את התוכנית המאומתת לקוד אובייקט, ולאחר מכן שלב קישור של הפלטפורמה יוצר תוצר .vxse.", + }, + ], + boundariesHeading: "היכן עוברים הגבולות היום", + boundaries: [ + { + label: "חוזה השפה הציבורי", + value: "הדוגמאות ב-Spec/ והמדריכים הקשורים ב-Documents/", + }, + { + label: "האמת של המימוש", + value: "בדיקות המהדר, תת־הקבוצה הנתמכת ואבחונים מפורשים", + }, + { + label: "הגדרת הפרויקט", + value: "Visual.XSharp.kts, שמפרשת מערכת הפרויקטים הכתובה ב-Kotlin", + }, + { + label: "האתר", + value: "היכרות עם השפה ומדריכים; בלי חשבונות ובלי ניהול חבילות", + }, + ], + questionHeading: "שאלות נפוצות", + questions: [ + { + q: "האם Visual X# מוכנה לסביבת ייצור?", + a: "אין כאן הצהרה כללית על מוכנות לסביבת ייצור. המהדר וסביבת זמן הריצה עדיין מתפתחים. כדי להעריך יכולת מסוימת, היעזרו בשערי הבדיקות של המאגר ובהערות הגרסה.", + }, + { + q: "האם המפרט הציבורי הוא חבילת בדיקות שאפשר להריץ?", + a: "לא במלואו. הוא כולל קטעים תקינים ושגויים מתוכננים. חלק מהדוגמאות מתעדות סמנטיקה לפני שקיימת לה תמיכה מלאה במהדר.", + }, + { + q: "למה להציג את שלבי הביניים?", + a: "לכל שלב יש בעלים וגבול אימות. המטרה היא שאפשר יהיה לבקר את התנהגות המהדר, ושהשלבים המאוחרים לא ימציאו משמעות לשפת המקור.", + }, + { + q: "האם Visual X# דורשת סביבת פיתוח מסוימת?", + a: "לא. תהליך הפיתוח הנתמך משתמש בכלי שורת פקודה ב-Windows וב-macOS; הפרויקט Xide הוא ממשק עתידי, לא דרישה מוקדמת.", + }, + ], + items: [ + { + title: "סמנטיקה לפני קיצורי דרך", + body: "לשפת המקור יש מפרט כתוב. המהדר מפריד בין תחביר מנותח, מפוענח ומוגדר טיפוסים, כך שהמעברים המאוחרים מקבלים משמעות מפורשת במקום לנחש.", + }, + { + title: "בעלות שאפשר לעקוב אחריה", + body: "ההתנהגות של הפניות ושל ערכים מוגדרת דרך הרכב הטיפוסים. ספירת הפניות אוטומטית וכללי העתקה־בעת־כתיבה הם חלק ממודל השפה; התמיכה בזמן ריצה ובהורדה עדיין בהשלמה.", + }, + { + title: "צינור מקומי מכוון", + body: "החזית מורידה את התוכנית דרך Core ו-CorePrep, ולאחר מכן Xpp ו-Xmm מתאימים אותה ליצירת הקוד של LLVM. הגבולות מאפשרים אימות ואבחונים בכל שלב.", + }, + ], + not: "מה היא איננה", + notText: + "Visual X# אינה מוצגת כתחליף מוגמר לשפות ייצור מבוססות. המפרט מתעד את הסמנטיקה המיועדת; את הכיסוי שהמהדר אכן בונה יש לבדוק בנפרד. אתר השפה הזה אינו מארח חשבונות או ניהול חבילות.", + next: "רוצים להתעמק?", + nextText: "התחילו בתוכנית קטנה, ואז עיינו במפרט הציבורי ובמאגר קוד המקור.", + start: "צעדים ראשונים", + spec: "לקריאת המפרט", + source: "לצפייה בקוד המקור", + }, } as const; const c = computed(() => text[locale.value]); </script> @@ -412,7 +526,7 @@ const c = computed(() => text[locale.value]); <p>{{ c.nextText }}</p> <div class="doc-actions"> <RouterLink class="button button-primary" to="/docs/getting-started" - >{{ c.start }} →</RouterLink + >{{ c.start }} {{ arrow }}</RouterLink ><a class="button button-quiet" href="https://github.com/Progmasoft/visual-xsharp/tree/main/Spec" diff --git a/frontend/src/preferences.ts b/frontend/src/preferences.ts index e5078bc..18d6615 100644 --- a/frontend/src/preferences.ts +++ b/frontend/src/preferences.ts @@ -1,32 +1,40 @@ // SPDX-FileCopyrightText: 2026 Progmasoft <support@progmasoft.com> // SPDX-License-Identifier: AGPL-3.0-or-later WITH AdditionRef-Progmasoft-Patent-Grant-1.1 -import { ref, watch } from "vue"; +import { computed, ref, watch } from "vue"; +import { forwardArrow, initialLocale, textDirection, type Locale } from "./locales"; -export const supportedLocales = ["en-US", "de-DE", "ru-RU"] as const; -export type Locale = (typeof supportedLocales)[number]; +export { supportedLocales, type Locale } from "./locales"; export type Theme = "dark" | "light"; -function storedLocale(): Locale { - const stored = localStorage.getItem("vxs-locale"); - return supportedLocales.find((supported) => supported === stored) ?? "en-US"; -} +// Holds a language only after the visitor picked one in the menu. The earlier +// `vxs-locale` key was written on every visit, so it cannot tell a choice +// from the former English default and is no longer read. +const localeChoiceKey = "vxs-locale-choice"; function storedTheme(): Theme { return localStorage.getItem("vxs-theme") === "light" ? "light" : "dark"; } -export const locale = ref<Locale>(storedLocale()); +export const locale = ref<Locale>( + initialLocale(localStorage.getItem(localeChoiceKey), navigator.languages ?? []), +); export const theme = ref<Theme>(storedTheme()); -watch( - locale, - (value) => { - document.documentElement.lang = value; - localStorage.setItem("vxs-locale", value); - }, - { immediate: true }, -); +/** The arrow that points onward in the current language. */ +export const arrow = computed(() => forwardArrow(locale.value)); + +function showLocale(value: Locale): void { + document.documentElement.lang = value; + document.documentElement.dataset.textDirection = textDirection(value); +} + +showLocale(locale.value); +// The language changes only through the menu, so a change is a choice. +watch(locale, (value) => { + showLocale(value); + localStorage.setItem(localeChoiceKey, value); +}); watch( theme, diff --git a/frontend/src/seo.ts b/frontend/src/seo.ts index 16b147d..c4e5e82 100644 --- a/frontend/src/seo.ts +++ b/frontend/src/seo.ts @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: 2026 Progmasoft <support@progmasoft.com> // SPDX-License-Identifier: AGPL-3.0-or-later WITH AdditionRef-Progmasoft-Patent-Grant-1.1 -import type { Locale } from "./preferences"; +import type { Locale } from "./locales"; export type PublicRoute = "/" | "/docs/overview" | "/docs/getting-started"; @@ -62,6 +62,23 @@ const metadata: Record<Locale, Record<PublicRoute, PageMetadata>> = { "Клонируйте Visual X#, проверьте набор инструментов, соберите и протестируйте компилятор и изучите первую программу с текущим CLI.", }, }, + "he-IL": { + "/": { + title: "Visual X# — להפוך את התוכנית כולה למובנת", + description: + "הכירו את Visual X#: שפת תכנות מקומית הבנויה סביב קוד מקור קריא, בעלות מפורשת וצינור מהדר שאפשר לבחון.", + }, + "/docs/overview": { + title: "סקירה · Visual X#", + description: + "יעדי התכנון של Visual X#, בחירות הטיפוסים והבעלות, שלבי המהדר והגבול בין המפרט למימוש.", + }, + "/docs/getting-started": { + title: "צעדים ראשונים · Visual X#", + description: + "שכפלו את Visual X#, בדקו את שרשרת הכלים, בנו ובדקו את המהדר ובחנו את התוכנית הראשונה עם ה-CLI הנוכחי.", + }, + }, }; function setMeta(selector: string, content: string): void { diff --git a/frontend/src/styles/site.css b/frontend/src/styles/site.css index f7e8a6b..96a1a0c 100644 --- a/frontend/src/styles/site.css +++ b/frontend/src/styles/site.css @@ -594,7 +594,7 @@ p { margin-left: auto; } .doc-layout { - grid-template-columns: 1fr; + grid-template-columns: minmax(0, 1fr); gap: 27px; } .doc-sidebar { @@ -656,3 +656,23 @@ p { animation-duration: 0.01ms !important; } } + +/* + * Right-to-left text. Only the text follows the direction of the language: + * the page layout, the navigation and code keep their left-to-right + * arrangement. Blocks of prose run right to left, and short pieces such as + * links, buttons and list items take the direction of their own first + * letter, so a product name on its own still reads left to right. + */ +[data-text-direction="rtl"] :is(h1, h2, h3, h4, h5, h6, p, blockquote, figcaption, dt, dd) { + direction: rtl; +} + +[data-text-direction="rtl"] :is(a, button, label, li, small, strong, span, option) { + unicode-bidi: plaintext; +} + +[data-text-direction="rtl"] :is(code, pre, kbd, samp) { + direction: ltr; + unicode-bidi: isolate; +} |