Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion src/shared/i18n/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
import { describe, expect, it } from "vitest";
import { t, getLocaleDirection } from "./index";
import { t, getLocaleDirection, resources } from "./index";

function flattenKeys(obj: Record<string, unknown>, prefix = ""): string[] {
return Object.entries(obj).flatMap(([key, value]) => {
const path = prefix ? `${prefix}.${key}` : key;
return value && typeof value === "object"
? flattenKeys(value as Record<string, unknown>, path)
: [path];
});
}

describe("shared i18n", () => {
it("returns English text by default", () => {
Expand Down Expand Up @@ -39,6 +48,16 @@ describe("shared i18n", () => {
expect(getLocaleDirection("en")).toBe("ltr");
});

it("keeps the he locale at full key parity with en", () => {
const enKeys = flattenKeys(
resources.en.translation as Record<string, unknown>,
);
const heKeys = new Set(
flattenKeys(resources.he.translation as Record<string, unknown>),
);
expect(enKeys.filter((key) => !heKeys.has(key))).toEqual([]);
});
Comment on lines +51 to +59

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 One-directional parity check

The test only asserts that every en key exists in he, but it does not check the reverse — so stale he-only keys (translations added to Hebrew but later removed from English, or mistakenly named keys) will accumulate silently without being caught. Over time this can lead to dead entries in the Hebrew locale with no CI signal. Adding a reverse check, heKeys.filter(k => !enKeys.has(k)), would catch this class of drift before it compounds.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!


it("falls back to en when zh-CN key is missing", () => {
expect(t("nonExistent.fallbackKey", "zh-CN")).toBe(
"nonExistent.fallbackKey",
Expand Down
14 changes: 14 additions & 0 deletions src/shared/i18n/locales/he/agents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,18 @@ export default {
manageProfiles: "ניהול פרופילים",
switchProfile: "החלפת פרופיל",
defaultTag: "ברירת מחדל",
editAppearance: "עריכת פרופיל",
editAppearanceFor: "עריכת {{name}}",
color: "צבע",
dangerZone: "אזור מסוכן",
deleteProfile: "מחיקת פרופיל",
deleteProfileInfo:
"מחיקת פרופיל זה מסירה לצמיתות את ההגדרות, הזיכרון, סשני הצ'אט, המיומנויות ומפתחות ה-API שלו. לא ניתן לבטל פעולה זו.",
deleteProfileConfirm:
"למחוק פרופיל זה לצמיתות? לא ניתן לבטל פעולה זו.",
edit: "עריכה",
uploadImage: "העלאת תמונה",
removeImage: "הסרת תמונה",
uploadImageFailed: "לא ניתן היה להגדיר את תמונת הפרופיל",
appearanceFailed: "לא ניתן היה לעדכן את מראה הפרופיל",
} as const;
12 changes: 12 additions & 0 deletions src/shared/i18n/locales/he/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export default {
quickAskTitle:
"שאלה מהירה (/btw) - שאלה צדדית שלא תשפיע על הקשר השיחה",
send: "שליחה",
searchModels: "חיפוש מודלים...",
custom: "מותאם אישית",
typeModelName: "הקלידו שם מודל...",
reasoningEffort: {
Expand Down Expand Up @@ -38,6 +39,14 @@ export default {
suggestionAnalyze: "ניתוח נתונים",
approve: "אישור",
deny: "דחייה",
clarify: {
defaultQuestion: "‏Hermes זקוק לקלט שלכם.",
placeholder: "הקלידו את תשובתכם… (Ctrl+Enter לשליחה)",
send: "שליחה",
skip: "דילוג - תנו ל-Hermes להחליט",
skipped: "דולג - Hermes החליט",
error: "לא ניתן היה למסור את תשובתכם - ייתכן שהתור הסתיים. נסו שוב.",
},
thinking: "חושב…",
thought: "מחשבה",
toolCall: "קריאה לכלי",
Expand Down Expand Up @@ -120,6 +129,9 @@ export default {
version: "הצגת גרסת Hermes",
},
queued: "{{count}} הודעות בתור - יישלחו בסיום עבודת הסוכן",
queuedCount: "{{count}} בתור",
queuedAttachment: "{{count}} צירופים",
queuedCancel: "הסרה מהתור",
worktree: {
loading: "טוען",
empty: "התיקייה ריקה",
Expand Down
4 changes: 3 additions & 1 deletion src/shared/i18n/locales/he/common.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
export default {
appName: "Hermes Agent",
appName: "Hermes One",
continue: "המשך",
cancel: "ביטול",
retry: "נסה שוב",
loading: "טוען...",
loadingShort: "טוען",
saved: "נשמר",
save: "שמירה",
done: "סיום",
edit: "עריכה",
search: "חיפוש",
searchPlaceholder: "חיפוש...",
show: "הצג",
Expand Down
41 changes: 41 additions & 0 deletions src/shared/i18n/locales/he/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ export default {
openrouterName: "OpenRouter",
openrouterDesc: "יותר מ-200 מודלים",
openrouterTag: "",
aimlapiName: "AIML API",
aimlapiDesc: "שער רב-מודלים תואם OpenAI",
anthropicName: "Anthropic",
anthropicDesc: "מודלים של Claude",
openaiName: "OpenAI",
Expand Down Expand Up @@ -33,10 +35,12 @@ export default {
vllm: "vLLM",
llamacpp: "llama.cpp",
groq: "Groq",
aimlapi: "AIML API",
deepseek: "DeepSeek",
together: "Together AI",
fireworks: "Fireworks",
cerebras: "Cerebras",
atlascloud: "AtlasCloud",
mistral: "Mistral",
// Theme
themeSystem: "מערכת",
Expand All @@ -49,6 +53,8 @@ export default {
sectionVoiceStt: "קול ו-STT",
sectionResearchTraining: "מחקר ואימון",
// Settings field labels
aimlapiApiKey: "מפתח API של AIML",
aimlapiHint: "מפתח API עבור AIML",
openrouterApiKey: "מפתח API של OpenRouter",
openrouterHint: "יותר מ-200 מודלים דרך OpenRouter (מומלץ)",
openaiApiKey: "מפתח API של OpenAI",
Expand Down Expand Up @@ -83,6 +89,8 @@ export default {
fireworksHint: "היסק מהיר למודלים פתוחים",
cerebrasApiKey: "מפתח API של Cerebras",
cerebrasHint: "היסק מהיר במיוחד על חומרת Cerebras",
atlascloudApiKey: "מפתח API של AtlasCloud",
atlascloudHint: "מודלים של Claude, GPT ומודלים פתוחים דרך AtlasCloud",
mistralApiKey: "מפתח API של Mistral",
mistralHint: "מודלים של Mistral ו-Codestral",
perplexityApiKey: "מפתח API של Perplexity",
Expand Down Expand Up @@ -119,6 +127,39 @@ export default {
tinkerHint: "שירות אימון RL",
wandbKey: "מפתח Weights & Biases",
wandbHint: "מעקב אחר ניסויים ומדדים",
// Auxiliary tasks
auxiliaryTitle: "משימות עזר",
auxiliaryDescription:
"משימות עזר מטפלות במשימות צד כמו ראייה, דחיסה וחילוץ מהאינטרנט. \"אוטומטי\" משמעו \"שימוש במודל הראשי\". עקפו לכל משימה כאשר תרצו מודל זול/מהיר למשימה מסוימת.",
auxiliaryResetAll: "איפוס הכול לאוטומטי",
auxiliaryVision: "ראייה",
auxiliaryVisionHint: "ניתוח תמונות/צילומי מסך",
auxiliaryWebExtract: "חילוץ מהאינטרנט",
auxiliaryWebExtractHint: "סיכום דפי אינטרנט",
auxiliaryCompression: "דחיסה",
auxiliaryCompressionHint: "סיכום הקשר",
auxiliarySkillsHub: "מרכז מיומנויות",
auxiliarySkillsHubHint: "חיפוש/התקנת מיומנויות",
auxiliaryApproval: "אישור",
auxiliaryApprovalHint: "אישור פקודות חכם",
auxiliaryMcp: "MCP",
auxiliaryMcpHint: "חשיבת כלי MCP",
auxiliaryTitleGeneration: "יצירת כותרות",
auxiliaryTitleGenerationHint: "כותרות סשנים",
auxiliaryTriageSpecifier: "מפרט מיון",
auxiliaryTriageSpecifierHint: "השלמת מפרט קנבן",
auxiliaryKanbanDecomposer: "מפרק קנבן",
auxiliaryKanbanDecomposerHint: "פירוק משימות",
auxiliaryProfileDescriber: "מתאר פרופילים",
auxiliaryProfileDescriberHint: "תיאורי פרופיל אוטומטיים",
auxiliaryCurator: "אוצר",
auxiliaryCuratorHint: "מעבר סקירת שימוש במיומנויות",
auxiliaryAuto: "אוטומטי",
auxiliaryProviderLabel: "ספק",
auxiliaryModelLabel: "מודל",
auxiliaryBaseUrlLabel: "כתובת URL בסיסית",
auxiliarySaved: "משימת העזר נשמרה",
auxiliaryResetSuccess: "כל משימות העזר אופסו לאוטומטי",
// Gateway section titles
gatewayMessagingPlatforms: "פלטפורמות מסרים",
// Gateway field labels
Expand Down
1 change: 1 addition & 0 deletions src/shared/i18n/locales/he/gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export default {
disable: "השבתה",
enable: "הפעלה",
enablePlatform: "הפעלת הפלטפורמה",
setupFirst: "הגדירו פרטי התחברות לפני הפעלת פלטפורמה זו",
strongWarning: "אזהרה חמורה",
riskDesc:
"‏{{name}} מאפשרת לפלטפורמת מסרים זו להפעיל כלים מקומיים רגישים. הפעילו אותה רק עבור ערוצים פרטיים ומהימנים ומשתמשים מוכרים.",
Expand Down
9 changes: 9 additions & 0 deletions src/shared/i18n/locales/he/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,13 @@ export default {
apiKeyLabel: "מפתח API",
apiKeyHint:
"נשמר כמשתנה סביבה. בוחר את מפתח הסביבה התואם לפי הכתובת, או CUSTOM_API_KEY אחרת.",
allProviders: "הכול",
browseRegistry: "עיון במודלים",
registryTitle: "מאגר מודלים",
registrySearchPlaceholder: "חיפוש ספקים ומודלים...",
registryAddButton: "הוספה",
registryAddedLabel: "נוסף",
registryCustomBadge: "דרך כתובת URL בסיסית",
registryAdded: "‏{{name}} נוסף למודלים שלכם",
registryLoadError: "טעינת מאגר המודלים נכשלה",
} as const;
2 changes: 2 additions & 0 deletions src/shared/i18n/locales/he/navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,6 @@ export default {
settings: "הגדרות",
collapseSidebar: "כווץ סרגל צד",
expandSidebar: "הרחב סרגל צד",
showRecentSessions: "הצג סשנים אחרונים",
hideRecentSessions: "הסתר סשנים אחרונים",
} as const;
1 change: 1 addition & 0 deletions src/shared/i18n/locales/he/sessions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export default {
empty: "אין סשנים עדיין",
newConversation: "שיחה חדשה",
newChat: "צ'אט חדש",
closeTab: "סגירת לשונית",
today: "היום",
yesterday: "אתמול",
thisWeek: "השבוע",
Expand Down
1 change: 1 addition & 0 deletions src/shared/i18n/locales/he/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export default {
together: "Together AI",
fireworks: "Fireworks",
cerebras: "Cerebras",
atlascloud: "AtlasCloud",
mistral: "Mistral",
},
serverPreset: "תבנית שרת",
Expand Down
Loading