feat(vscode): add Persian (Farsi) UI language (#12424)

* feat(vscode): add Persian (Farsi) UI language

Add Farsi (fa) as a first-class UI locale for the VS Code extension, with
automatic right-to-left layout (the extension already supports RTL via Arabic).

- Register fa in language-utils (Locale union, LOCALES, RTL_LOCALES),
  language.tsx (label + merged dict layers), kiloclaw, the extension-host and
  cli-backend translation bundles, and both package.json language enums.
- Add complete fa.ts dictionaries for all kilo-vscode i18n namespaces
  (sidebar, agent manager, kiloclaw, extension host, autocomplete, cli-backend).
- Extend i18n tests to enforce fa key completeness and placeholder alignment.

Persian language contributed by Babak Safabahar.

* fix(vscode): translate remaining Persian UI strings

* fix(vscode): make question dock and suggestions RTL-aware

The interactive question dock pinned option labels/descriptions to the left
via a hardcoded text-align: left, so they rendered left-aligned even under an
RTL locale (Persian/Arabic). Use logical text-align: start and logical padding
on the header, and add dir=auto to question text, option label/description,
collapsed preview, review rows and the suggestion text so mixed-script content
renders with correct bidi.

Contributed by Babak Safabahar.

* fix(i18n): sync Persian translations with latest en.ts

Resync fa.ts after rebasing on main:
- Translate the 27 newly added English keys into Persian.
- Remove the 23 obsolete memory.* keys that no longer exist in en.ts.
- Translate error.chain.configDirectoryTypo and other single-quoted values
  that were previously left in English.

All {{placeholder}} tokens are preserved; brand/technical terms (MCP, VS Code,
Vercel, Kilo Code, TerminalBench) intentionally stay untranslated. The i18n
key-completeness and placeholder-alignment tests pass.

Contributed by Babak Safabahar.

* fix(i18n): sync Persian translations with latest main

Translate the 45 newly added keys after rebasing on upstream main, including
the four session.prompts.* keys requested by the reviewer:
  - session.prompts.navLabel  -> ناوبر پرامپت
  - session.prompts.tick      -> پرامپت {{index}} از {{total}}: {{prompt}}
  - session.prompts.noAnswer  -> هنوز پاسخی وجود ندارد
  - session.prompts.queued    -> در صف انتظار

All {{placeholder}} tokens preserved; brand terms stay untranslated.
i18n tests pass (32/32). Contributed by Babak Safabahar.

* fix(i18n): improve contextual accuracy of 8 Persian translations

Revise the 8 keys flagged by the reviewer as too literal:
  - migration.whatsNew.features.agentManager.title: was 'مدیر عامل'; now 'Agent Manager' (product name, not translated)
  - agentManager.dialog.createWorktree: was 'ایجاد درخت‌کاری'; now 'ایجاد Worktree' (git technical term)
  - agentManager.dialog.versionHint: 'worktree' kept in English
  - sidebar.session.configureWorktree.tooltip: 'worktree'/'Agent Manager' kept in English
  - sidebar.session.showChanges.tooltip.empty: 'working tree' kept in English
  - diffViewer.source.unstaged.tooltip: 'working tree'/'stage' kept in English
  - settings.language.description: phrasing improved
  - prompt.placeholder.default: already natural; minor polish

Also sync fa.ts with any additional new keys added since the last commit.
i18n tests pass (32/32). Contributed by Babak Safabahar.

* Update packages/kilo-vscode/webview-ui/src/i18n/fa.ts

Co-authored-by: kilo-code-bot[bot] <240665456+kilo-code-bot[bot]@users.noreply.github.com>

---------

Co-authored-by: Johnny Eric Amancio <johnnyeric@gmail.com>
Co-authored-by: kilo-code-bot[bot] <240665456+kilo-code-bot[bot]@users.noreply.github.com>
This commit is contained in:
Babak S
2026-07-29 16:51:22 +03:30
committed by GitHub
parent bba2dba0a8
commit d6380b246b
19 changed files with 1693 additions and 14 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"kilo-code": minor
---
Add Persian (Farsi) as a UI language, including right-to-left layout. Contributed by Babak Safabahar.
+8 -4
View File
@@ -868,7 +868,8 @@
"tr",
"nl",
"uk",
"it"
"it",
"fa"
],
"enumDescriptions": [
"Auto (VS Code language)",
@@ -891,7 +892,8 @@
"Türkçe",
"Nederlands",
"Українська",
"Italiano"
"Italiano",
"فارسی"
]
},
"kilo-code.new.languageCommitMessage": {
@@ -919,7 +921,8 @@
"tr",
"nl",
"uk",
"it"
"it",
"fa"
],
"enumDescriptions": [
"跟随界面语言 (Sync with UI language)",
@@ -942,7 +945,8 @@
"Türkçe",
"Nederlands",
"Українська",
"Italiano"
"Italiano",
"فارسی"
]
},
"kilo-code.new.model.providerID": {
@@ -0,0 +1,6 @@
export const dict = {
"server.processExited": "فرآیند CLI با کد {{code}} قبل از راه‌اندازی سرور خاتمه یافت",
"server.startupTimeout": "زمان راه‌اندازی سرور پس از {{seconds}} ثانیه به پایان رسید",
"remote.connected": "Kilo Remote: متصل شد",
"remote.connecting": "Kilo Remote: در حال اتصال…",
} as const
@@ -6,6 +6,7 @@ import { dict as da } from "./da"
import { dict as de } from "./de"
import { dict as en } from "./en"
import { dict as es } from "./es"
import { dict as fa } from "./fa"
import { dict as fr } from "./fr"
import { dict as it } from "./it"
import { dict as ja } from "./ja"
@@ -29,6 +30,7 @@ const bundles: Record<string, Record<string, string>> = {
de,
en,
es,
fa,
fr,
it,
ja,
@@ -0,0 +1,27 @@
// English runtime translations for autocomplete (kilocode:autocomplete.* namespace)
// Source: src/i18n/locales/en/kilocode.json → "autocomplete" section
export const dict = {
"kilocode:autocomplete.statusBar.enabled": "$(kilo-logo) تکمیل خودکار",
"kilocode:autocomplete.statusBar.snoozed": "به تعویق افتاده",
"kilocode:autocomplete.statusBar.warning": "$(warning) تکمیل خودکار",
"kilocode:autocomplete.statusBar.tooltip.basic": "تکمیل خودکار Kilo Code",
"kilocode:autocomplete.statusBar.tooltip.noUsableProvider":
"**هیچ مدل تکمیل خودکاری پیکربندی نشده است**\n\nبرای فعال‌سازی تکمیل خودکار، یک پروفایل با یکی از ارائه‌دهندگان پشتیبانی‌شده زیر اضافه کنید: {{providers}}.\n\n[باز کردن تنظیمات]({{command}})",
"kilocode:autocomplete.statusBar.tooltip.completionSummary":
"{{count}} تکمیل بین {{startTime}} و {{endTime}} انجام شد، با هزینه کل {{cost}}.",
"kilocode:autocomplete.statusBar.tooltip.providerInfo":
"تکمیل خودکار توسط {{model}} از طریق {{provider}} ارائه می‌شود.",
"kilocode:autocomplete.statusBar.cost.zero": "۰.۰۰$",
"kilocode:autocomplete.statusBar.cost.lessThanCent": "<۰.۰۱$",
"kilocode:autocomplete.codeAction.title": "Kilo Code: ویرایش‌های پیشنهادی",
"kilocode:autocomplete.incompatibilityExtensionPopup.message":
"تکمیل خودکار Kilo Code به دلیل تعارض با GitHub Copilot مسدود شده است. برای رفع این مشکل، باید پیشنهادات درون‌خطی Copilot را غیرفعال کنید.",
"kilocode:autocomplete.incompatibilityExtensionPopup.disableCopilot": "غیرفعال کردن Copilot",
"kilocode:autocomplete.incompatibilityExtensionPopup.disableInlineAssist": "غیرفعال کردن تکمیل خودکار",
"kilocode:autocomplete.creditsExhausted.message":
"تکمیل خودکار Kilo Code متوقف شده است. دلایل احتمالی: حساب Kilo شما اعتبار کافی ندارد، یا کلید API پیکربندی‌شده (BYOK) به سقف مجاز خود رسیده است. برای از سرگیری تکمیل خودکار، اعتبار Kilo اضافه کنید یا تنظیمات کلید API خود را بررسی کنید.",
"kilocode:autocomplete.creditsExhausted.addCredits": "افزودن اعتبار",
"kilocode:autocomplete.authError.message":
"تکمیل خودکار Kilo Code به دلیل مشکل احراز هویت متوقف شده است. دلایل احتمالی: وارد Kilo نشده‌اید، یا کلید API (BYOK) شما نامعتبر یا وارد نشده است. لطفاً دوباره وارد شوید یا تنظیمات کلید API ارائه‌دهنده خود را بررسی کنید.",
}
+7
View File
@@ -0,0 +1,7 @@
import { dict as autocompleteDict } from "./autocomplete/fa"
export { autocompleteDict }
export const dict = {
...autocompleteDict,
} as const
@@ -6,6 +6,7 @@ import { dict as de } from "./de"
import { dict as en } from "./en"
import { type dict as enDict } from "./en"
import { dict as es } from "./es"
import { dict as fa } from "./fa"
import { dict as fr } from "./fr"
import { dict as it } from "./it"
import { dict as ja } from "./ja"
@@ -28,6 +29,7 @@ const bundles: Record<string, Record<string, string>> = {
de,
en,
es,
fa,
fr,
it,
ja,
@@ -39,6 +39,7 @@ import { dict as amTr } from "../../webview-ui/agent-manager/i18n/tr"
import { dict as amNl } from "../../webview-ui/agent-manager/i18n/nl"
import { dict as amUk } from "../../webview-ui/agent-manager/i18n/uk"
import { dict as amIt } from "../../webview-ui/agent-manager/i18n/it"
import { dict as amFa } from "../../webview-ui/agent-manager/i18n/fa"
const PREFIX = "agentManager."
@@ -63,6 +64,7 @@ const locales = {
nl: amNl,
uk: amUk,
it: amIt,
fa: amFa,
}
const appLocales = {
@@ -41,6 +41,7 @@ import { dict as appTr } from "../../webview-ui/src/i18n/tr"
import { dict as appNl } from "../../webview-ui/src/i18n/nl"
import { dict as appUk } from "../../webview-ui/src/i18n/uk"
import { dict as appIt } from "../../webview-ui/src/i18n/it"
import { dict as appFa } from "../../webview-ui/src/i18n/fa"
// Layer 2: upstream UI (@opencode-ai/ui re-exported via @kilocode/kilo-ui)
import { dict as uiEn } from "../../../ui/src/i18n/en"
@@ -114,6 +115,7 @@ import { dict as cliTr } from "../../src/services/cli-backend/i18n/tr"
import { dict as cliNl } from "../../src/services/cli-backend/i18n/nl"
import { dict as cliUk } from "../../src/services/cli-backend/i18n/uk"
import { dict as cliIt } from "../../src/services/cli-backend/i18n/it"
import { dict as cliFa } from "../../src/services/cli-backend/i18n/fa"
import { dict as hostEn } from "../../src/services/i18n/en"
import { dict as hostZh } from "../../src/services/i18n/zh"
@@ -135,6 +137,7 @@ import { dict as hostTr } from "../../src/services/i18n/tr"
import { dict as hostNl } from "../../src/services/i18n/nl"
import { dict as hostUk } from "../../src/services/i18n/uk"
import { dict as hostIt } from "../../src/services/i18n/it"
import { dict as hostFa } from "../../src/services/i18n/fa"
// ── Locale maps ─────────────────────────────────────────────────────────────
@@ -161,6 +164,7 @@ const appLocales: Record<string, Record<string, string>> = {
nl: appNl,
uk: appUk,
it: appIt,
fa: appFa,
}
const kiloLocales: Record<string, Record<string, string>> = {
@@ -230,6 +234,7 @@ const cliLocales: Record<string, Record<string, string>> = {
nl: cliNl,
uk: cliUk,
it: cliIt,
fa: cliFa,
}
const hostLocales: Record<string, Record<string, string>> = {
@@ -253,6 +258,7 @@ const hostLocales: Record<string, Record<string, string>> = {
nl: hostNl,
uk: hostUk,
it: hostIt,
fa: hostFa,
}
// Merge webview dictionaries in the same priority order as language.tsx
@@ -33,6 +33,11 @@ describe("normalizeLocale", () => {
expect(normalizeLocale("ko-KR")).toBe("ko")
})
it("returns 'fa' for Persian", () => {
expect(normalizeLocale("fa")).toBe("fa")
expect(normalizeLocale("fa-IR")).toBe("fa")
})
it("returns 'no' for Norwegian Bokmål", () => {
expect(normalizeLocale("nb")).toBe("no")
expect(normalizeLocale("nb-NO")).toBe("no")
+207
View File
@@ -0,0 +1,207 @@
export const dict = {
"agentManager.local": "محلی",
"agentManager.sidebar.collapse": "بستن نوار کناری",
"agentManager.sidebar.expand": "نمایش نوار کناری",
"agentManager.section.worktrees": "WORKTREES",
"agentManager.section.sessions": "SESSIONS",
"agentManager.notGitRepo": "این یک مخزن git نیست",
"agentManager.worktree.settings": "تنظیمات Worktree",
"agentManager.worktree.new": "Worktree جدید",
"agentManager.worktree.setupScript": "اسکریپت راه‌اندازی Worktree",
"agentManager.worktree.delete": "حذف Worktree",
"agentManager.worktree.confirmDelete": "حذف شود؟",
"agentManager.worktree.stale": "منسوخ",
"agentManager.worktree.staleTooltip": "روی دیسک وجود ندارد یا دیگر توسط git worktree ردیابی نمی‌شود",
"agentManager.worktree.removeStale": "حذف worktree منسوخ",
"agentManager.worktree.doubleClickRename": "برای تغییر نام دوبار کلیک کنید",
"agentManager.worktree.versions": "{{count}} نسخه",
"agentManager.worktree.advancedOptions": "گزینه‌های پیشرفته worktree",
"agentManager.worktree.defaultBaseBranch": "شاخه پایه پیش‌فرض",
"agentManager.worktree.defaultBaseBranchAuto": "تشخیص خودکار",
"agentManager.worktree.copyPath": "کپی مسیر",
"agentManager.worktree.openInVscode": "باز کردن در VS Code",
"agentManager.worktree.rename": "تغییر نام",
"agentManager.worktree.newSection": "بخش جدید",
"agentManager.worktree.ungrouped": "بدون گروه",
"agentManager.section.rename": "تغییر نام بخش",
"agentManager.section.setColor": "تنظیم رنگ",
"agentManager.section.delete": "حذف بخش",
"agentManager.section.defaultName": "بخش جدید",
"agentManager.section.moveUp": "انتقال به بالا",
"agentManager.section.moveDown": "انتقال به پایین",
"agentManager.hoverCard.branch": "شاخه",
"agentManager.hoverCard.worktree": "Worktree",
"agentManager.hoverCard.base": "پایه",
"agentManager.hoverCard.sessions": "جلسات",
"agentManager.hoverCard.files": "فایل‌ها",
"agentManager.hoverCard.changes": "تغییرات",
"agentManager.hoverCard.commits": "کامیت‌ها",
"agentManager.session.new": "جلسه جدید",
"agentManager.session.untitled": "بدون عنوان",
"agentManager.session.newSession": "جلسه جدید",
"agentManager.session.openInWorktree": "باز کردن در worktree",
"agentManager.session.openLocally": "باز کردن به صورت محلی",
"agentManager.session.readonly": "جلسه فقط‌خواندنی",
"agentManager.session.noSessions": "هیچ جلسه‌ای باز نیست",
"agentManager.tab.close": "بستن",
"agentManager.tab.closeOthers": "بستن بقیه",
"agentManager.tab.closeTab": "بستن برگه",
"agentManager.tab.forkSession": "انشعاب جلسه",
"agentManager.tab.terminal": "ترمینال",
"agentManager.tab.openTerminal": "باز کردن ترمینال",
"agentManager.tab.newOptions": "گزینه‌های بیشتر برای تب جدید",
"agentManager.tabsMenu.status.waiting": "انتظار",
"agentManager.tabsMenu.status.retry": "تلاش مجدد",
"agentManager.sidebarSearch.label": "جستجوی worktree‌ها و جلسات",
"agentManager.sidebarSearch.scope": "جستجو در فضای کاری محلی، جلسات محلی، worktree‌ها و جلسات آن‌ها",
"agentManager.sidebarSearch.contexts": "محلی و WORKTREE‌ها",
"agentManager.terminal.new": "تب ترمینال جدید",
"agentManager.terminal.add": "ترمینال جدید",
"agentManager.terminal.ended": "ترمینال پایان یافت — برای بستن، تب را ببندید",
"agentManager.terminal.connectionError": "خطای اتصال ترمینال",
"agentManager.terminal.empty": "هنوز ترمینالی اینجا وجود ندارد",
"agentManager.terminal.start": "شروع ترمینال",
"agentManager.terminal.destination": "انتخاب کنید دکمه ترمینال چه چیزی را باز کند",
"agentManager.terminal.openInVscode": "ترمینال VS Code",
"agentManager.terminal.openInPanel": "پنل Agent Manager",
"agentManager.terminal.errorTitle": "خطای ترمینال",
"agentManager.setup.failed": "راه‌اندازی Worktree ناموفق بود",
"agentManager.setup.settingUp": "در حال راه‌اندازی Worktree",
"agentManager.setup.error.git_not_found":
"Git نصب نشده یا در PATH یافت نشد. لطفاً Git را نصب کرده و VS Code را مجدداً راه‌اندازی کنید.",
"agentManager.setup.error.not_git_repo": "برای استفاده از Worktree، پوشه‌ای که حاوی یک مخزن git است را باز کنید.",
"agentManager.setup.error.lfs_missing":
"این مخزن از Git LFS استفاده می‌کند، اما git-lfs یافت نشد. لطفاً Git LFS را نصب کنید.",
"agentManager.setup.error.no_commits":
"این مخزن هنوز هیچ کامیتی ندارد. قبل از استفاده از Worktree، یک کامیت اولیه ایجاد کنید.",
"agentManager.shortcuts.title": "میانبرهای صفحه‌کلید",
"agentManager.shortcuts.category.sidebar": "نوار کناری",
"agentManager.shortcuts.category.tabs": "تب‌ها",
"agentManager.shortcuts.category.terminal": "ترمینال",
"agentManager.shortcuts.category.global": "سراسری",
"agentManager.shortcuts.previousItem": "مورد قبلی",
"agentManager.shortcuts.nextItem": "مورد بعدی",
"agentManager.shortcuts.newWorktree": "Worktree جدید",
"agentManager.shortcuts.advancedWorktree": "پیکربندی Worktree جدید",
"agentManager.shortcuts.openWorktree": "باز کردن Worktree",
"agentManager.shortcuts.openPR": "باز کردن pull request",
"agentManager.shortcuts.deleteWorktree": "حذف Worktree",
"agentManager.shortcuts.previousTab": "تب قبلی",
"agentManager.shortcuts.nextTab": "تب بعدی",
"agentManager.shortcuts.newTab": "تب جدید",
"agentManager.shortcuts.closeTab": "بستن تب",
"agentManager.shortcuts.toggleTerminal": "نمایش/پنهان کردن ترمینال",
"agentManager.shortcuts.runScript": "اجرای اسکریپت",
"agentManager.run.options": "گزینه‌های اجرا",
"agentManager.run.configure": "پیکربندی اسکریپت اجرا",
"agentManager.shortcuts.openAgentManager": "باز کردن Agent Manager",
"agentManager.shortcuts.cycleAgentMode": "حالت عامل بعدی",
"agentManager.shortcuts.cyclePreviousAgentMode": "حالت عامل قبلی",
"agentManager.shortcuts.showShortcuts": "نمایش میانبرهای صفحه‌کلید",
"agentManager.dialog.removeStaleWorktree.title": "حذف Worktree قدیمی",
"agentManager.dialog.removeStaleWorktree.messagePre": "حذف Worktree قدیمی ",
"agentManager.dialog.removeStaleWorktree.messagePost":
"؟ این عملیات فقط نگاشت Agent Manager را حذف می‌کند و فایل‌های روی دیسک دست‌نخورده باقی می‌مانند.",
"agentManager.dialog.removeStaleWorktree.cancel": "لغو",
"agentManager.dialog.removeStaleWorktree.confirm": "حذف Worktree قدیمی",
"agentManager.dialog.openWorktree": "Worktree جدید",
"agentManager.dialog.tab.new": "جدید",
"agentManager.dialog.tab.import": "وارد کردن",
"agentManager.dialog.namePlaceholder": "نام Worktree (اختیاری)",
"agentManager.dialog.promptPlaceholder.mac": "پیامی بنویسید (⌘Enter برای ارسال)",
"agentManager.dialog.promptPlaceholder.other": "پیامی بنویسید (Ctrl+Enter برای ارسال)",
"agentManager.dialog.advancedOptions": "گزینه‌های پیشرفته",
"agentManager.dialog.branchName": "نام شاخه",
"agentManager.dialog.branchNamePlaceholder": "تولید خودکار",
"agentManager.dialog.baseBranch": "شاخه پایه",
"agentManager.dialog.searchBranches": "جستجوی شاخه‌ها...",
"agentManager.dialog.branchBadge.default": "پیش‌فرض",
"agentManager.dialog.branchBadge.remote": "راه دور",
"agentManager.dialog.versions": "نسخه‌ها",
"agentManager.dialog.versionHint": "{{count}} worktree به صورت موازی اجرا خواهند شد",
"agentManager.dialog.compareModels": "مقایسه مدل‌ها",
"agentManager.dialog.compareModels.tooltip": "اجرای عامل‌ها روی مدل‌های مختلف به صورت موازی برای مقایسه نتایج",
"agentManager.dialog.compareModels.searchModels": "جستجوی مدل‌ها...",
"agentManager.dialog.compareModels.selectModels": "انتخاب مدل‌ها...",
"agentManager.dialog.compareModels.effort": "میزان استدلال",
"agentManager.dialog.compareModels.effortDefault": "پیش‌فرض",
"agentManager.dialog.creating": "در حال ایجاد...",
"agentManager.dialog.createWorktree": "ایجاد Worktree",
"agentManager.dialog.removeImage": "حذف تصویر",
"agentManager.dialog.configureWorktree": "پیکربندی Worktree جدید...",
"agentManager.diff.toggle": "تغییر وضعیت diff",
"agentManager.diff.openFile": "باز کردن فایل",
"agentManager.diff.revertFile": "بازگردانی فایل",
"agentManager.diff.revertSuccess": "فایل بازگردانی شد",
"agentManager.diff.revertError": "بازگردانی ناموفق بود",
"agentManager.open.button": "باز کردن",
"agentManager.open.tooltip": "باز کردن این Worktree در VS Code",
"agentManager.apply.globalButton": "اعمال",
"agentManager.apply.tooltip": "اعمال تغییرات worktree انتخاب‌شده به شاخه محلی",
"agentManager.apply.dialogTitle": "اعمال تغییرات به شاخه محلی",
"agentManager.apply.confirm": "همین الان اعمال کن",
"agentManager.apply.selectionSummary": "{{selected}} از {{total}} فایل انتخاب‌شده",
"agentManager.apply.selectAll": "انتخاب همه",
"agentManager.apply.selectNone": "هیچ‌کدام را انتخاب نکن",
"agentManager.apply.checking": "در حال بررسی...",
"agentManager.apply.applying": "در حال اعمال...",
"agentManager.apply.success": "تغییرات اعمال شد",
"agentManager.apply.conflict": "اعمال دارای تعارض است",
"agentManager.apply.conflictToast": "{{count}} تعارض در {{files}} فایل. جزئیات را در Apply بررسی کنید.",
"agentManager.apply.error": "اعمال ناموفق بود",
"agentManager.apply.conflictsTitle": "تعارض‌ها",
"agentManager.apply.unknownFile": "فایل نامشخص",
"agentManager.apply.reason.index": "فایل محلی با نسخه مورد انتظار تفاوت دارد",
"agentManager.apply.reason.patch": "پچ به‌درستی قابل اعمال نیست",
"agentManager.apply.reason.contents": "خواندن محتوای فایل محلی فعلی ممکن نیست",
"agentManager.apply.reason.unknown": "تعارض شناسایی شد",
"agentManager.shortcuts.toggleDiff": "نمایش/پنهان کردن پنل diff",
"agentManager.shortcuts.category.quickSwitch": "تغییر سریع",
"agentManager.shortcuts.jumpToItem": "پرش به مورد ۱–۹",
"agentManager.review.sendAllToChat": "ارسال همه به چت",
"agentManager.review.sendAllToChatWithCount": "ارسال همه به چت ({{count}})",
"agentManager.review.sendAllShortcut.mac": "⌘Enter",
"agentManager.review.sendAllShortcut.other": "Ctrl+Enter",
"agentManager.review.modalTitle": "بررسی نظر",
"agentManager.review.inlineCount": "نظرات بررسی ({{count}})",
"agentManager.review.clearAll": "پاک کردن همه",
"agentManager.review.commentOnLine": "نظر در خط {{line}}",
"agentManager.review.editCommentOnLine": "ویرایش نظر در خط {{line}}",
"agentManager.review.commentPlaceholder": "یک نظر بگذارید...",
"agentManager.review.commentAction": "نظر",
"agentManager.review.sendToChat": "ارسال به چت",
"agentManager.review.metaFile": "فایل",
"agentManager.review.metaLine": "خط",
"agentManager.review.metaComment": "نظر کاربر",
"agentManager.review.collapsedOnly": "{{count}} جمع‌شده",
"agentManager.review.collapsedWithLarge": "{{collapsed}} جمع‌شده، {{large}} بزرگ",
"agentManager.review.largeFileCollapsed": "فایل بزرگ (جمع‌شده)",
"agentManager.review.image": "تصویر",
"agentManager.review.imageBefore": "قبل",
"agentManager.review.imageAfter": "بعد",
"agentManager.review.imageTooLarge": "تصویر برای پیش‌نمایش خیلی بزرگ است ({{size}}).",
"agentManager.review.imageUnreadable": "این تصویر قابل نمایش نیست.",
"agentManager.review.imageUnavailable": "پیش‌نمایش تصویر برای این نمونه جلسه در دسترس نیست.",
"agentManager.review.endOfLongDiff": "به انتها رسیدید!",
"agentManager.import.pullRequest": "Pull Request",
"agentManager.import.pastePrUrl": "URL درخواست PR را وارد کنید...",
"agentManager.import.open": "باز کردن",
"agentManager.import.branches": "شاخه‌ها",
"agentManager.import.selectBranch": "انتخاب شاخه...",
"agentManager.import.loading": "در حال بارگذاری...",
"agentManager.import.loadingBranches": "در حال بارگذاری شاخه‌ها...",
"agentManager.import.noMatchingBranches": "شاخه‌ای مطابق یافت نشد",
"agentManager.import.noBranchesFound": "هیچ شاخه‌ای یافت نشد.",
"agentManager.import.noBranchesHint": "یک URL درخواست ادغام را در بالا جای‌گذاری کنید یا یک worktree جدید بسازید.",
"agentManager.import.failed": "وارد کردن ناموفق بود",
}
@@ -27,6 +27,7 @@ import { dict as tr } from "../i18n/tr"
import { dict as zh } from "../i18n/zh"
import { dict as uk } from "../i18n/uk"
import { dict as zht } from "../i18n/zht"
import { dict as fa } from "../i18n/fa"
const dicts: Record<Locale, Record<string, string>> = {
en,
@@ -49,6 +50,7 @@ const dicts: Record<Locale, Record<string, string>> = {
uk: { ...en, ...uk },
zh: { ...en, ...zh },
zht: { ...en, ...zht },
fa: { ...en, ...fa },
}
type LanguageCtx = {
+95
View File
@@ -0,0 +1,95 @@
export const dict = {
// App
"kiloClaw.loading": "در حال بارگذاری KiloClaw...",
// Chat
"kiloClaw.chat.connecting": "در حال اتصال...",
"kiloClaw.chat.notRunning": "نمونه در حال اجرا نیست",
"kiloClaw.chat.placeholder": "پیامی بنویسید... (Enter برای ارسال)",
"kiloClaw.chat.online": "آنلاین",
"kiloClaw.chat.offline": "آفلاین",
"kiloClaw.chat.idle": "بیکار",
"kiloClaw.chat.unknown": "نامشخص",
"kiloClaw.chat.empty": "هنوز پیامی وجود ندارد. سلام کنید!",
"kiloClaw.chat.emptyWithBot": "هنوز پیامی وجود ندارد. به {bot} سلام کنید!",
"kiloClaw.chat.send": "ارسال",
"kiloClaw.chat.waitingBotStatus": "در انتظار وضعیت ربات...",
"kiloClaw.chat.botOffline": "ربات آفلاین است",
// Conversations
"kiloClaw.conversations.title": "مکالمات",
"kiloClaw.conversations.new": "مکالمه جدید",
"kiloClaw.conversations.empty": "هنوز مکالمه‌ای وجود ندارد",
"kiloClaw.conversations.untitled": "بدون عنوان",
"kiloClaw.conversations.rename": "تغییر نام",
"kiloClaw.conversations.leave": "خروج",
"kiloClaw.conversations.selectOne": "یک مکالمه را برای شروع گفتگو انتخاب کنید",
"kiloClaw.conversations.groupToday": "امروز",
"kiloClaw.conversations.groupYesterday": "دیروز",
"kiloClaw.conversations.groupThisWeek": "این هفته",
"kiloClaw.conversations.groupOlder": "قدیمی‌تر",
// Typing
"kiloClaw.typing.one": "{name} در حال تایپ است...",
"kiloClaw.typing.many": "{count} نفر در حال تایپ هستند...",
// Messages
"kiloClaw.message.bot": "KiloClaw",
"kiloClaw.message.you": "شما",
"kiloClaw.message.thinking": "در حال فکر کردن...",
"kiloClaw.message.deleted": "[پیام حذف شد]",
"kiloClaw.message.replyDeleted": "پیام اصلی حذف شد",
"kiloClaw.message.notDelivered": "ارسال نشد",
"kiloClaw.message.edited": "(ویرایش شده)",
"kiloClaw.message.reply": "پاسخ",
"kiloClaw.message.replyTo": "در حال پاسخ به",
"kiloClaw.message.cancelReply": "لغو پاسخ",
"kiloClaw.message.edit": "ویرایش",
"kiloClaw.message.delete": "حذف",
"kiloClaw.message.confirmDelete": "این پیام حذف شود؟",
"kiloClaw.message.cancel": "انصراف",
"kiloClaw.message.save": "ذخیره",
"kiloClaw.message.react": "واکنش",
"kiloClaw.message.removeReaction": "حذف واکنش",
"kiloClaw.message.copy": "کپی",
"kiloClaw.message.copied": "در کلیپ‌بورد کپی شد",
"kiloClaw.message.copyFailed": "کپی انجام نشد",
// Error
"kiloClaw.error.retry": "تلاش مجدد",
// Setup
"kiloClaw.setup.title": "KiloClaw",
"kiloClaw.setup.subtitle": "هوش مصنوعی شخصی برای زندگی روزمره",
"kiloClaw.setup.description1":
"KiloClaw یک هوش مصنوعی شخصی در اختیار شما قرار می‌دهد که ایمیل‌ها را می‌خواند، تقویم شما را مدیریت می‌کند، پروژه‌هایتان را رصد می‌کند و در Telegram، Slack — هر چیزی که از قبل استفاده می‌کنید — در دسترس است.",
"kiloClaw.setup.description2":
"نیازی به نصب برنامه نیست. رابط جدیدی برای یادگیری وجود ندارد. فقط مثل یک دوست برایش پیام بفرستید.",
"kiloClaw.setup.learnMore": "بیشتر بدانید",
"kiloClaw.setup.tryKiloClaw": "امتحان KiloClaw",
// Upgrade
"kiloClaw.upgrade.title": "KiloClaw Chat نیاز به ارتقا دارد",
"kiloClaw.upgrade.description1": "این نمونه قبل از فعال‌سازی چت راه‌اندازی شده است.",
"kiloClaw.upgrade.description2.before": "از دکمه ",
"kiloClaw.upgrade.description2.bold": "ارتقا به آخرین نسخه",
"kiloClaw.upgrade.description2.after": " در داشبورد KiloClaw برای فعال‌سازی چت زنده با ربات خود استفاده کنید.",
"kiloClaw.upgrade.openDashboard": "باز کردن داشبورد",
// Sidebar
"kiloClaw.sidebar.title": "KiloClaw",
"kiloClaw.sidebar.instance": "نمونه",
"kiloClaw.sidebar.unknown": "ناشناخته",
"kiloClaw.sidebar.bot": "ربات",
"kiloClaw.sidebar.botStatus": "وضعیت ربات",
"kiloClaw.sidebar.context": "زمینه",
"kiloClaw.sidebar.used": "استفاده‌شده",
"kiloClaw.sidebar.tokens": "توکن‌ها",
"kiloClaw.sidebar.model": "مدل",
"kiloClaw.sidebar.provider": "ارائه‌دهنده",
"kiloClaw.sidebar.details": "جزئیات",
"kiloClaw.sidebar.region": "منطقه",
"kiloClaw.sidebar.version": "نسخه",
"kiloClaw.sidebar.channels": "کانال‌ها",
"kiloClaw.sidebar.noData": "داده‌ای برای نمونه وجود ندارد",
}
@@ -348,7 +348,9 @@ export const QuestionDock: Component<{ request: QuestionRequest }> = (props) =>
<div data-slot="question-dock-header-content">
<div data-slot="question-header-title">{summary()}</div>
<Show when={store.collapsed}>
<div data-slot="question-collapsed-preview">{questionText()}</div>
<div data-slot="question-collapsed-preview" dir="auto">
{questionText()}
</div>
</Show>
</div>
<div data-slot="question-header-actions" onClick={(e: MouseEvent) => e.stopPropagation()}>
@@ -391,7 +393,9 @@ export const QuestionDock: Component<{ request: QuestionRequest }> = (props) =>
<div data-slot="question-dock-body" inert={store.collapsed || undefined}>
<div data-slot="question-dock-body-inner">
<Show when={!confirm()}>
<div data-slot="question-text">{questionText()}</div>
<div data-slot="question-text" dir="auto">
{questionText()}
</div>
<Show when={multi()} fallback={<div data-slot="question-hint">{language.t("ui.question.singleHint")}</div>}>
<div data-slot="question-hint">{language.t("ui.question.multiHint")}</div>
</Show>
@@ -419,9 +423,13 @@ export const QuestionDock: Component<{ request: QuestionRequest }> = (props) =>
</span>
</span>
<span data-slot="question-option-main">
<span data-slot="option-label">{localized.label()}</span>
<span data-slot="option-label" dir="auto">
{localized.label()}
</span>
<Show when={localized.description()}>
<span data-slot="option-description">{localized.description()}</span>
<span data-slot="option-description" dir="auto">
{localized.description()}
</span>
</Show>
</span>
</button>
@@ -498,8 +506,10 @@ export const QuestionDock: Component<{ request: QuestionRequest }> = (props) =>
const answered = () => Boolean(value())
return (
<div data-slot="review-item">
<span data-slot="review-label">{tr(language.t, q.questionKey, q.question)}</span>
<span data-slot="review-value" data-answered={answered()}>
<span data-slot="review-label" dir="auto">
{tr(language.t, q.questionKey, q.question)}
</span>
<span data-slot="review-value" data-answered={answered()} dir="auto">
{answered() ? value() : language.t("ui.question.review.notAnswered")}
</span>
</div>
@@ -30,7 +30,9 @@ export const SuggestBar: Component<{ request: SuggestionRequest }> = (props) =>
<span data-slot="suggest-bar-icon">
<Icon name="brain" size="small" />
</span>
<span data-slot="suggest-bar-text">{props.request.text}</span>
<span data-slot="suggest-bar-text" dir="auto">
{props.request.text}
</span>
</div>
<Show when={error()}>
<div data-slot="suggest-bar-error">
@@ -19,9 +19,10 @@ export type Locale =
| "nl"
| "uk"
| "it"
| "fa"
/** Locales that use right-to-left script. */
export const RTL_LOCALES = new Set<Locale>(["ar"])
export const RTL_LOCALES = new Set<Locale>(["ar", "fa"])
/** Map internal locale IDs to valid BCP 47 language tags for the HTML lang attribute. */
export const LOCALE_BCP47: Partial<Record<Locale, string>> = {
@@ -55,6 +56,7 @@ export const LOCALES: readonly Locale[] = [
"nl",
"uk",
"it",
"fa",
]
/**
@@ -49,6 +49,7 @@ import { dict as appTr } from "../i18n/tr"
import { dict as appNl } from "../i18n/nl"
import { dict as appUk } from "../i18n/uk"
import { dict as appIt } from "../i18n/it"
import { dict as appFa } from "../i18n/fa"
import { dict as amEn } from "../../agent-manager/i18n/en"
import { dict as amZh } from "../../agent-manager/i18n/zh"
import { dict as amZht } from "../../agent-manager/i18n/zht"
@@ -69,6 +70,7 @@ import { dict as amTr } from "../../agent-manager/i18n/tr"
import { dict as amNl } from "../../agent-manager/i18n/nl"
import { dict as amUk } from "../../agent-manager/i18n/uk"
import { dict as amIt } from "../../agent-manager/i18n/it"
import { dict as amFa } from "../../agent-manager/i18n/fa"
import { dict as kiloEn } from "@kilocode/kilo-i18n/en"
import { dict as kiloZh } from "@kilocode/kilo-i18n/zh"
import { dict as kiloZht } from "@kilocode/kilo-i18n/zht"
@@ -118,6 +120,7 @@ export const LOCALE_LABELS: Record<Locale, string> = {
nl: "Nederlands",
uk: "Українська",
it: "Italiano",
fa: "فارسی",
}
// Merge 4 dict layers: app + ui + kilo + agent manager (kilo and agent manager override last)
@@ -143,6 +146,9 @@ const dicts: Record<Locale, Record<string, string>> = {
nl: { ...base, ...appNl, ...uiNl, ...kiloNl, ...amEn, ...amNl },
uk: { ...base, ...appUk, ...uiUk, ...kiloUk, ...amEn, ...amUk },
it: { ...base, ...appIt, ...uiIt, ...kiloIt, ...amEn, ...amIt },
// Persian (Kilo fork addition). Only app + agent-manager layers are localized;
// the upstream ui/kilo layers fall back to English via `base`.
fa: { ...base, ...appFa, ...amEn, ...amFa },
}
function normalizeLocale(lang: string): Locale {
File diff suppressed because it is too large Load Diff
@@ -29,7 +29,8 @@
align-items: center;
justify-content: space-between;
gap: 6px;
padding: 6px 8px 4px 12px;
padding-block: 6px 4px;
padding-inline: 12px 8px;
cursor: pointer;
}
@@ -176,7 +177,7 @@
background-color: transparent;
border: none;
border-radius: 6px;
text-align: left;
text-align: start;
width: 100%;
cursor: pointer;
outline: none;