Compare commits

...

30 Commits

Author SHA1 Message Date
Adam
fbd5eff77a chore(docs): i18n sync 2026-02-27 19:18:55 -06:00
Alex Yaroshuk
2a2082233d fix(app): display skill name in skill tool call (#15413) 2026-02-27 19:18:14 -06:00
Adam
267d2c82de chore: cleanup 2026-02-27 19:12:19 -06:00
Jay
0b8c1f1f7d docs: Update OpenCode Go subscription and usage details (#15415) 2026-02-27 16:04:53 -08:00
Frank
2eb1d4cb9a doc: go 2026-02-27 18:03:39 -05:00
Frank
d2a8f44c22 doc: opencode go 2026-02-27 17:38:30 -05:00
opencode-agent[bot]
1f1f36aac1 chore: update nix node_modules hashes 2026-02-27 21:59:23 +00:00
Adam
7f851da15e chore(console): i18n sync (#15360) 2026-02-27 15:50:50 -06:00
Adam
a3bdb974b3 chore(app): deps 2026-02-27 15:49:38 -06:00
opencode-agent[bot]
46d678fce9 chore: generate 2026-02-27 21:17:37 +00:00
Alex Yaroshuk
1f2348c1ef fix(app): make bash output selectable (#15378) 2026-02-27 15:16:33 -06:00
shivam kr chaudhary
f347194e31 docs: add missing Bosanski link to Arabic README (#15399) 2026-02-27 15:15:48 -06:00
opencode-agent[bot]
7ff2710ce3 chore: generate 2026-02-27 20:37:28 +00:00
James Long
c12ce2ffff feat(core): basic implementation of remote workspace support (#15120) 2026-02-27 15:36:39 -05:00
Adam
a94f564ff0 fix(app): scroll issues 2026-02-27 09:47:56 -06:00
Adam
6ef3af73df chore(app): i18n sync (#15362) 2026-02-27 09:45:00 -06:00
Adam
e5ae6c51b0 chore: update translator model 2026-02-27 09:27:12 -06:00
Adam
9d76ef6c66 chore: update docs locale sync workflow 2026-02-27 09:18:17 -06:00
Kit Langton
e49e781cb8 feat(app): add Warp to the open menu (#15368) 2026-02-27 09:44:01 -05:00
Kit Langton
78cea89e0e chore(script): source team members from TEAM_MEMBERS (#15369) 2026-02-27 09:43:51 -05:00
James Long
3dc10a1c16 Change keybindings to navigate between child sessions (#14814) 2026-02-27 09:41:23 -05:00
Adam
157920b2fb chore: update test 2026-02-27 06:54:15 -06:00
Brendan Allan
967313234a desktop: add latest.json finalizer script (#15335) 2026-02-27 20:18:21 +08:00
Adam
dfa0281117 fix(app): auto-accept permissions 2026-02-27 06:17:40 -06:00
Adam
4a94096994 fix(app): update provider sprite 2026-02-27 06:01:00 -06:00
opencode-agent[bot]
3407ded9d0 chore: generate 2026-02-27 11:41:55 +00:00
vaur94
a325c9af8f feat(app): add Turkish (tr) locale for app and ui packages (#15278)
Co-authored-by: Ugur <ugur@example.com>
2026-02-27 05:41:06 -06:00
Pirro Zani
dc8c011510 docs(readme): add Greek translation and update language navigation (#15281) 2026-02-27 05:39:56 -06:00
Filip
1f108bc401 feat(app): recent projects section in command pallette (#15270) 2026-02-27 05:33:22 -06:00
Frank
6b3118883c wip: zen 2026-02-27 02:56:43 -05:00
353 changed files with 12300 additions and 5695 deletions

View File

@@ -47,12 +47,14 @@ jobs:
echo "EOF"
} >> "$GITHUB_OUTPUT"
- name: Install OpenCode
if: steps.changes.outputs.has_changes == 'true'
run: curl -fsSL https://opencode.ai/install | bash
- name: Sync locale docs with OpenCode
if: steps.changes.outputs.has_changes == 'true'
uses: sst/opencode/github@latest
env:
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
GITHUB_TOKEN: ${{ steps.committer.outputs.token }}
OPENCODE_CONFIG_CONTENT: |
{
"permission": {
@@ -96,11 +98,8 @@ jobs:
}
}
}
with:
model: opencode/gpt-5.3-codex
agent: docs
use_github_token: true
prompt: |
run: |
opencode run --agent docs --model opencode/gpt-5.3-codex <<'EOF'
Update localized docs to match the latest English docs changes.
Changed English doc files:
@@ -118,6 +117,7 @@ jobs:
7. Keep locale docs structure aligned with their corresponding English pages.
8. Do not modify English source docs in packages/web/src/content/docs/*.mdx.
9. If no locale updates are needed, make no changes.
EOF
- name: Commit and push locale docs updates
if: steps.changes.outputs.has_changes == 'true'

View File

@@ -1,7 +1,7 @@
---
description: Translate content for a specified locale while preserving technical terms
mode: subagent
model: opencode/gemini-3.1-pro
model: opencode/gemini-3-pro
---
You are a professional translator and localization specialist.

View File

@@ -27,13 +27,15 @@
<a href="README.ja.md">日本語</a> |
<a href="README.pl.md">Polski</a> |
<a href="README.ru.md">Русский</a> |
<a href="README.bs.md">Bosanski</a> |
<a href="README.ar.md">العربية</a> |
<a href="README.no.md">Norsk</a> |
<a href="README.br.md">Português (Brasil)</a> |
<a href="README.th.md">ไทย</a> |
<a href="README.tr.md">Türkçe</a> |
<a href="README.uk.md">Українська</a> |
<a href="README.bn.md">বাংলা</a>
<a href="README.bn.md">বাংলা</a> |
<a href="README.gr.md">Ελληνικά</a>
</p>
[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai)

View File

@@ -34,7 +34,8 @@
<a href="README.th.md">ไทย</a> |
<a href="README.tr.md">Türkçe</a> |
<a href="README.uk.md">Українська</a> |
<a href="README.bn.md">বাংলা</a>
<a href="README.bn.md">বাংলা</a> |
<a href="README.gr.md">Ελληνικά</a>
</p>
[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai)

View File

@@ -33,7 +33,8 @@
<a href="README.th.md">ไทย</a> |
<a href="README.tr.md">Türkçe</a> |
<a href="README.uk.md">Українська</a> |
<a href="README.bn.md">বাংলা</a>
<a href="README.bn.md">বাংলা</a> |
<a href="README.gr.md">Ελληνικά</a>
</p>
[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai)

View File

@@ -34,7 +34,8 @@
<a href="README.th.md">ไทย</a> |
<a href="README.tr.md">Türkçe</a> |
<a href="README.uk.md">Українська</a> |
<a href="README.bn.md">বাংলা</a>
<a href="README.bn.md">বাংলা</a> |
<a href="README.gr.md">Ελληνικά</a>
</p>
[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai)

View File

@@ -33,7 +33,8 @@
<a href="README.th.md">ไทย</a> |
<a href="README.tr.md">Türkçe</a> |
<a href="README.uk.md">Українська</a> |
<a href="README.bn.md">বাংলা</a>
<a href="README.bn.md">বাংলা</a> |
<a href="README.gr.md">Ελληνικά</a>
</p>
[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai)

View File

@@ -33,7 +33,8 @@
<a href="README.th.md">ไทย</a> |
<a href="README.tr.md">Türkçe</a> |
<a href="README.uk.md">Українська</a> |
<a href="README.bn.md">বাংলা</a>
<a href="README.bn.md">বাংলা</a> |
<a href="README.gr.md">Ελληνικά</a>
</p>
[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai)

View File

@@ -33,7 +33,8 @@
<a href="README.th.md">ไทย</a> |
<a href="README.tr.md">Türkçe</a> |
<a href="README.uk.md">Українська</a> |
<a href="README.bn.md">বাংলা</a>
<a href="README.bn.md">বাংলা</a> |
<a href="README.gr.md">Ελληνικά</a>
</p>
[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai)

View File

@@ -33,7 +33,8 @@
<a href="README.th.md">ไทย</a> |
<a href="README.tr.md">Türkçe</a> |
<a href="README.uk.md">Українська</a> |
<a href="README.bn.md">বাংলা</a>
<a href="README.bn.md">বাংলা</a> |
<a href="README.gr.md">Ελληνικά</a>
</p>
[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai)

140
README.gr.md Normal file
View File

@@ -0,0 +1,140 @@
<p align="center">
<a href="https://opencode.ai">
<picture>
<source srcset="packages/console/app/src/asset/logo-ornate-dark.svg" media="(prefers-color-scheme: dark)">
<source srcset="packages/console/app/src/asset/logo-ornate-light.svg" media="(prefers-color-scheme: light)">
<img src="packages/console/app/src/asset/logo-ornate-light.svg" alt="OpenCode logo">
</picture>
</a>
</p>
<p align="center">Ο πράκτορας τεχνητής νοημοσύνης ανοικτού κώδικα για προγραμματισμό.</p>
<p align="center">
<a href="https://opencode.ai/discord"><img alt="Discord" src="https://img.shields.io/discord/1391832426048651334?style=flat-square&label=discord" /></a>
<a href="https://www.npmjs.com/package/opencode-ai"><img alt="npm" src="https://img.shields.io/npm/v/opencode-ai?style=flat-square" /></a>
<a href="https://github.com/anomalyco/opencode/actions/workflows/publish.yml"><img alt="Build status" src="https://img.shields.io/github/actions/workflow/status/anomalyco/opencode/publish.yml?style=flat-square&branch=dev" /></a>
</p>
<p align="center">
<a href="README.md">English</a> |
<a href="README.zh.md">简体中文</a> |
<a href="README.zht.md">繁體中文</a> |
<a href="README.ko.md">한국어</a> |
<a href="README.de.md">Deutsch</a> |
<a href="README.es.md">Español</a> |
<a href="README.fr.md">Français</a> |
<a href="README.it.md">Italiano</a> |
<a href="README.da.md">Dansk</a> |
<a href="README.ja.md">日本語</a> |
<a href="README.pl.md">Polski</a> |
<a href="README.ru.md">Русский</a> |
<a href="README.bs.md">Bosanski</a> |
<a href="README.ar.md">العربية</a> |
<a href="README.no.md">Norsk</a> |
<a href="README.br.md">Português (Brasil)</a> |
<a href="README.th.md">ไทย</a> |
<a href="README.tr.md">Türkçe</a> |
<a href="README.uk.md">Українська</a> |
<a href="README.bn.md">বাংলা</a> |
<a href="README.gr.md">Ελληνικά</a>
</p>
[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai)
---
### Εγκατάσταση
```bash
# YOLO
curl -fsSL https://opencode.ai/install | bash
# Διαχειριστές πακέτων
npm i -g opencode-ai@latest # ή bun/pnpm/yarn
scoop install opencode # Windows
choco install opencode # Windows
brew install anomalyco/tap/opencode # macOS και Linux (προτείνεται, πάντα ενημερωμένο)
brew install opencode # macOS και Linux (επίσημος τύπος brew, λιγότερο συχνές ενημερώσεις)
sudo pacman -S opencode # Arch Linux (Σταθερό)
paru -S opencode-bin # Arch Linux (Τελευταία έκδοση από AUR)
mise use -g opencode # Οποιοδήποτε λειτουργικό σύστημα
nix run nixpkgs#opencode # ή github:anomalyco/opencode με βάση την πιο πρόσφατη αλλαγή από το dev branch
```
> [!TIP]
> Αφαίρεσε παλαιότερες εκδόσεις από τη 0.1.x πριν από την εγκατάσταση.
### Εφαρμογή Desktop (BETA)
Το OpenCode είναι επίσης διαθέσιμο ως εφαρμογή. Κατέβασε το απευθείας από τη [σελίδα εκδόσεων](https://github.com/anomalyco/opencode/releases) ή το [opencode.ai/download](https://opencode.ai/download).
| Πλατφόρμα | Λήψη |
| --------------------- | ------------------------------------- |
| macOS (Apple Silicon) | `opencode-desktop-darwin-aarch64.dmg` |
| macOS (Intel) | `opencode-desktop-darwin-x64.dmg` |
| Windows | `opencode-desktop-windows-x64.exe` |
| Linux | `.deb`, `.rpm`, ή AppImage |
```bash
# macOS (Homebrew)
brew install --cask opencode-desktop
# Windows (Scoop)
scoop bucket add extras; scoop install extras/opencode-desktop
```
#### Κατάλογος Εγκατάστασης
Το script εγκατάστασης τηρεί την ακόλουθη σειρά προτεραιότητας για τη διαδρομή εγκατάστασης:
1. `$OPENCODE_INSTALL_DIR` - Προσαρμοσμένος κατάλογος εγκατάστασης
2. `$XDG_BIN_DIR` - Διαδρομή συμβατή με τις προδιαγραφές XDG Base Directory
3. `$HOME/bin` - Τυπικός κατάλογος εκτελέσιμων αρχείων χρήστη (εάν υπάρχει ή μπορεί να δημιουργηθεί)
4. `$HOME/.opencode/bin` - Προεπιλεγμένη εφεδρική διαδρομή
```bash
# Παραδείγματα
OPENCODE_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash
XDG_BIN_DIR=$HOME/.local/bin curl -fsSL https://opencode.ai/install | bash
```
### Πράκτορες
Το OpenCode περιλαμβάνει δύο ενσωματωμένους πράκτορες μεταξύ των οποίων μπορείτε να εναλλάσσεστε με το πλήκτρο `Tab`.
- **build** - Προεπιλεγμένος πράκτορας με πλήρη πρόσβαση για εργασία πάνω σε κώδικα
- **plan** - Πράκτορας μόνο ανάγνωσης για ανάλυση και εξερεύνηση κώδικα
- Αρνείται την επεξεργασία αρχείων από προεπιλογή
- Ζητά άδεια πριν εκτελέσει εντολές bash
- Ιδανικός για εξερεύνηση άγνωστων αρχείων πηγαίου κώδικα ή σχεδιασμό αλλαγών
Περιλαμβάνεται επίσης ένας **general** υποπράκτορας για σύνθετες αναζητήσεις και πολυβηματικές διεργασίες.
Χρησιμοποιείται εσωτερικά και μπορεί να κληθεί χρησιμοποιώντας `@general` στα μηνύματα.
Μάθετε περισσότερα για τους [πράκτορες](https://opencode.ai/docs/agents).
### Οδηγός Χρήσης
Για περισσότερες πληροφορίες σχετικά με τη ρύθμιση του OpenCode, [**πλοηγήσου στον οδηγό χρήσης μας**](https://opencode.ai/docs).
### Συνεισφορά
Εάν ενδιαφέρεσαι να συνεισφέρεις στο OpenCode, διαβάστε τα [οδηγό χρήσης συνεισφοράς](./CONTRIBUTING.md) πριν υποβάλεις ένα pull request.
### Δημιουργία πάνω στο OpenCode
Εάν εργάζεσαι σε ένα έργο σχετικό με το OpenCode και χρησιμοποιείτε το "opencode" ως μέρος του ονόματός του, για παράδειγμα "opencode-dashboard" ή "opencode-mobile", πρόσθεσε μια σημείωση στο README σας για να διευκρινίσεις ότι δεν είναι κατασκευασμένο από την ομάδα του OpenCode και δεν έχει καμία σχέση με εμάς.
### Συχνές Ερωτήσεις
#### Πώς διαφέρει αυτό από το Claude Code;
Είναι πολύ παρόμοιο με το Claude Code ως προς τις δυνατότητες. Ακολουθούν οι βασικές διαφορές:
- 100% ανοιχτού κώδικα
- Δεν είναι συνδεδεμένο με κανέναν πάροχο. Αν και συνιστούμε τα μοντέλα που παρέχουμε μέσω του [OpenCode Zen](https://opencode.ai/zen), το OpenCode μπορεί να χρησιμοποιηθεί με Claude, OpenAI, Google, ή ακόμα και τοπικά μοντέλα. Καθώς τα μοντέλα εξελίσσονται, τα κενά μεταξύ τους θα κλείσουν και οι τιμές θα μειωθούν, οπότε είναι σημαντικό να είσαι ανεξάρτητος από τον πάροχο.
- Out-of-the-box υποστήριξη LSP
- Εστίαση στο TUI. Το OpenCode είναι κατασκευασμένο από χρήστες που χρησιμοποιούν neovim και τους δημιουργούς του [terminal.shop](https://terminal.shop)· θα εξαντλήσουμε τα όρια του τι είναι δυνατό στο terminal.
- Αρχιτεκτονική client/server. Αυτό, για παράδειγμα, μπορεί να επιτρέψει στο OpenCode να τρέχει στον υπολογιστή σου ενώ το χειρίζεσαι εξ αποστάσεως από μια εφαρμογή κινητού, που σημαίνει ότι το TUI frontend είναι μόνο ένας από τους πιθανούς clients.
---
**Γίνε μέλος της κοινότητάς μας** [Discord](https://discord.gg/opencode) | [X.com](https://x.com/opencode)

View File

@@ -33,7 +33,8 @@
<a href="README.th.md">ไทย</a> |
<a href="README.tr.md">Türkçe</a> |
<a href="README.uk.md">Українська</a> |
<a href="README.bn.md">বাংলা</a>
<a href="README.bn.md">বাংলা</a> |
<a href="README.gr.md">Ελληνικά</a>
</p>
[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai)

View File

@@ -33,7 +33,8 @@
<a href="README.th.md">ไทย</a> |
<a href="README.tr.md">Türkçe</a> |
<a href="README.uk.md">Українська</a> |
<a href="README.bn.md">বাংলা</a>
<a href="README.bn.md">বাংলা</a> |
<a href="README.gr.md">Ελληνικά</a>
</p>
[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai)

View File

@@ -33,7 +33,8 @@
<a href="README.th.md">ไทย</a> |
<a href="README.tr.md">Türkçe</a> |
<a href="README.uk.md">Українська</a> |
<a href="README.bn.md">বাংলা</a>
<a href="README.bn.md">বাংলা</a> |
<a href="README.gr.md">Ελληνικά</a>
</p>
[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai)

View File

@@ -34,7 +34,8 @@
<a href="README.th.md">ไทย</a> |
<a href="README.tr.md">Türkçe</a> |
<a href="README.uk.md">Українська</a> |
<a href="README.bn.md">বাংলা</a>
<a href="README.bn.md">বাংলা</a> |
<a href="README.gr.md">Ελληνικά</a>
</p>
[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai)

View File

@@ -33,7 +33,8 @@
<a href="README.th.md">ไทย</a> |
<a href="README.tr.md">Türkçe</a> |
<a href="README.uk.md">Українська</a> |
<a href="README.bn.md">বাংলা</a>
<a href="README.bn.md">বাংলা</a> |
<a href="README.gr.md">Ελληνικά</a>
</p>
[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai)

View File

@@ -33,7 +33,8 @@
<a href="README.th.md">ไทย</a> |
<a href="README.tr.md">Türkçe</a> |
<a href="README.uk.md">Українська</a> |
<a href="README.bn.md">বাংলা</a>
<a href="README.bn.md">বাংলা</a> |
<a href="README.gr.md">Ελληνικά</a>
</p>
[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai)

View File

@@ -33,7 +33,8 @@
<a href="README.th.md">ไทย</a> |
<a href="README.tr.md">Türkçe</a> |
<a href="README.uk.md">Українська</a> |
<a href="README.bn.md">বাংলা</a>
<a href="README.bn.md">বাংলা</a> |
<a href="README.gr.md">Ελληνικά</a>
</p>
[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai)

View File

@@ -33,7 +33,8 @@
<a href="README.th.md">ไทย</a> |
<a href="README.tr.md">Türkçe</a> |
<a href="README.uk.md">Українська</a> |
<a href="README.bn.md">বাংলা</a>
<a href="README.bn.md">বাংলা</a> |
<a href="README.gr.md">Ελληνικά</a>
</p>
[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai)

View File

@@ -33,7 +33,8 @@
<a href="README.th.md">ไทย</a> |
<a href="README.tr.md">Türkçe</a> |
<a href="README.uk.md">Українська</a> |
<a href="README.bn.md">বাংলা</a>
<a href="README.bn.md">বাংলা</a> |
<a href="README.gr.md">Ελληνικά</a>
</p>
[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai)

View File

@@ -34,7 +34,8 @@
<a href="README.th.md">ไทย</a> |
<a href="README.tr.md">Türkçe</a> |
<a href="README.uk.md">Українська</a> |
<a href="README.bn.md">বাংলা</a>
<a href="README.bn.md">বাংলা</a> |
<a href="README.gr.md">Ελληνικά</a>
</p>
[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai)

View File

@@ -33,7 +33,8 @@
<a href="README.th.md">ไทย</a> |
<a href="README.tr.md">Türkçe</a> |
<a href="README.uk.md">Українська</a> |
<a href="README.bn.md">বাংলা</a>
<a href="README.bn.md">বাংলা</a> |
<a href="README.gr.md">Ελληνικά</a>
</p>
[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai)

View File

@@ -33,7 +33,8 @@
<a href="README.th.md">ไทย</a> |
<a href="README.tr.md">Türkçe</a> |
<a href="README.uk.md">Українська</a> |
<a href="README.bn.md">বাংলা</a>
<a href="README.bn.md">বাংলা</a> |
<a href="README.gr.md">Ελληνικά</a>
</p>
[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai)

View File

@@ -545,7 +545,7 @@
"@kobalte/core": "0.13.11",
"@octokit/rest": "22.0.0",
"@openauthjs/openauth": "0.0.0-20250322224806",
"@pierre/diffs": "1.1.0-beta.13",
"@pierre/diffs": "1.1.0-beta.18",
"@playwright/test": "1.51.0",
"@solid-primitives/storage": "4.3.3",
"@solidjs/meta": "0.29.4",
@@ -1469,7 +1469,9 @@
"@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.5.1", "", { "os": "win32", "cpu": "x64" }, "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA=="],
"@pierre/diffs": ["@pierre/diffs@1.1.0-beta.13", "", { "dependencies": { "@shikijs/transformers": "^3.0.0", "diff": "8.0.3", "hast-util-to-html": "9.0.5", "lru_map": "0.4.1", "shiki": "^3.0.0" }, "peerDependencies": { "react": "^18.3.1 || ^19.0.0", "react-dom": "^18.3.1 || ^19.0.0" } }, "sha512-D35rxDu5V7XHX5aVGU6PF12GhscL+I+9QYgxK/i3h0d2XSirAxDdVNm49aYwlOhgmdvL0NbS1IHxPswVB5yJvw=="],
"@pierre/diffs": ["@pierre/diffs@1.1.0-beta.18", "", { "dependencies": { "@pierre/theme": "0.0.22", "@shikijs/transformers": "^3.0.0", "diff": "8.0.3", "hast-util-to-html": "9.0.5", "lru_map": "0.4.1", "shiki": "^3.0.0" }, "peerDependencies": { "react": "^18.3.1 || ^19.0.0", "react-dom": "^18.3.1 || ^19.0.0" } }, "sha512-7ZF3YD9fxdbYsPnltz5cUqHacN7ztp8RX/fJLxwv8wIEORpP4+7dHz1h/qx3o4EW2xUrIhmbM8ImywLasB787Q=="],
"@pierre/theme": ["@pierre/theme@0.0.22", "", {}, "sha512-ePUIdQRNGjrveELTU7fY89Xa7YGHHEy5Po5jQy/18lm32eRn96+tnYJEtFooGdffrx55KBUtOXfvVy/7LDFFhA=="],
"@pinojs/redact": ["@pinojs/redact@0.4.0", "", {}, "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg=="],

View File

@@ -1,8 +1,8 @@
{
"nodeModules": {
"x86_64-linux": "sha256-dZoLhWe4smBsOF7WczMySLXSAB1YRO1vfhiOCL1rBf0=",
"aarch64-linux": "sha256-J7nIz1xuVZEHun5WRZkYRySz29B0A8g5g0RRxnIWTYU=",
"aarch64-darwin": "sha256-R2PuhX+EjUBuLE8MF0G0fcUwNaU+5n6V6uVeK89ulzw=",
"x86_64-darwin": "sha256-Bvzfz9TsTpYriZNLSLgpNcNb+BgtkgpjoWqdOtF2IBg="
"x86_64-linux": "sha256-2XLuizbG90QDUQL+1M90XxfVZxjkIQ1cFYS46nnVO7g=",
"aarch64-linux": "sha256-hlckiGAtbpAlwgcE7KgzKKRq9T2FEOSq3Q1MhuHfZ2c=",
"aarch64-darwin": "sha256-V/8Kay+5bDb/BSVgBQhSMwzmRmkNGl3U0HFMVbVcMak=",
"x86_64-darwin": "sha256-duLDF88Q/hXK5jwBy4dVxMSiTTS0R4obp9MlTuOF/Pw="
}
}

View File

@@ -35,7 +35,7 @@
"@tsconfig/bun": "1.0.9",
"@cloudflare/workers-types": "4.20251008.0",
"@openauthjs/openauth": "0.0.0-20250322224806",
"@pierre/diffs": "1.1.0-beta.13",
"@pierre/diffs": "1.1.0-beta.18",
"@solid-primitives/storage": "4.3.3",
"@tailwindcss/vite": "4.1.11",
"diff": "8.0.2",

View File

@@ -50,6 +50,15 @@ async function clearPermissionDock(page: any, label: RegExp) {
}
}
async function setAutoAccept(page: any, enabled: boolean) {
const button = page.locator('[data-action="prompt-permissions"]').first()
await expect(button).toBeVisible()
const pressed = (await button.getAttribute("aria-pressed")) === "true"
if (pressed === enabled) return
await button.click()
await expect(button).toHaveAttribute("aria-pressed", enabled ? "true" : "false")
}
async function withMockPermission<T>(
page: any,
request: {
@@ -168,6 +177,7 @@ test("blocked question flow unblocks after submit", async ({ page, sdk, gotoSess
test("blocked permission flow supports allow once", async ({ page, sdk, gotoSession }) => {
await withDockSession(sdk, "e2e composer dock permission once", async (session) => {
await gotoSession(session.id)
await setAutoAccept(page, false)
await withMockPermission(
page,
{
@@ -195,6 +205,7 @@ test("blocked permission flow supports allow once", async ({ page, sdk, gotoSess
test("blocked permission flow supports reject", async ({ page, sdk, gotoSession }) => {
await withDockSession(sdk, "e2e composer dock permission reject", async (session) => {
await gotoSession(session.id)
await setAutoAccept(page, false)
await withMockPermission(
page,
{
@@ -221,6 +232,7 @@ test("blocked permission flow supports reject", async ({ page, sdk, gotoSession
test("blocked permission flow supports allow always", async ({ page, sdk, gotoSession }) => {
await withDockSession(sdk, "e2e composer dock permission always", async (session) => {
await gotoSession(session.id)
await setAutoAccept(page, false)
await withMockPermission(
page,
{
@@ -300,6 +312,7 @@ test("child session permission request blocks parent dock and supports allow onc
}) => {
await withDockSession(sdk, "e2e composer dock child permission parent", async (session) => {
await gotoSession(session.id)
await setAutoAccept(page, false)
const child = await sdk.session
.create({

View File

@@ -2,6 +2,7 @@ import { createSignal } from "solid-js"
import { Dialog } from "@opencode-ai/ui/dialog"
import { Button } from "@opencode-ai/ui/button"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { useLanguage } from "@/context/language"
import { useSettings } from "@/context/settings"
export type Highlight = {
@@ -16,6 +17,7 @@ export type Highlight = {
export function DialogReleaseNotes(props: { highlights: Highlight[] }) {
const dialog = useDialog()
const language = useLanguage()
const settings = useSettings()
const [index, setIndex] = createSignal(0)
@@ -83,16 +85,16 @@ export function DialogReleaseNotes(props: { highlights: Highlight[] }) {
<div class="flex flex-col items-start gap-3">
{isLast() ? (
<Button variant="primary" size="large" onClick={handleClose}>
Get started
{language.t("dialog.releaseNotes.action.getStarted")}
</Button>
) : (
<Button variant="secondary" size="large" onClick={handleNext}>
Next
{language.t("dialog.releaseNotes.action.next")}
</Button>
)}
<Button variant="ghost" size="small" onClick={handleDisable}>
Don't show these in the future
{language.t("dialog.releaseNotes.action.hideFuture")}
</Button>
</div>
@@ -128,7 +130,7 @@ export function DialogReleaseNotes(props: { highlights: Highlight[] }) {
{feature()!.media!.type === "image" ? (
<img
src={feature()!.media!.src}
alt={feature()!.media!.alt ?? feature()?.title ?? "Release preview"}
alt={feature()!.media!.alt ?? feature()?.title ?? language.t("dialog.releaseNotes.media.alt")}
class="w-full h-full object-cover"
/>
) : (

View File

@@ -8,6 +8,7 @@ import fuzzysort from "fuzzysort"
import { createMemo, createResource, createSignal } from "solid-js"
import { useGlobalSDK } from "@/context/global-sdk"
import { useGlobalSync } from "@/context/global-sync"
import { useLayout } from "@/context/layout"
import { useLanguage } from "@/context/language"
interface DialogSelectDirectoryProps {
@@ -19,6 +20,7 @@ interface DialogSelectDirectoryProps {
type Row = {
absolute: string
search: string
group: "recent" | "folders"
}
function cleanInput(value: string) {
@@ -101,7 +103,7 @@ function displayPath(path: string, input: string, home: string) {
return tildeOf(full, home) || full
}
function toRow(absolute: string, home: string): Row {
function toRow(absolute: string, home: string, group: Row["group"]): Row {
const full = trimTrailing(absolute)
const tilde = tildeOf(full, home)
const withSlash = (value: string) => {
@@ -113,7 +115,16 @@ function toRow(absolute: string, home: string): Row {
const search = Array.from(
new Set([full, withSlash(full), tilde, withSlash(tilde), getFilename(full)].filter(Boolean)),
).join("\n")
return { absolute: full, search }
return { absolute: full, search, group }
}
function uniqueRows(rows: Row[]) {
const seen = new Set<string>()
return rows.filter((row) => {
if (seen.has(row.absolute)) return false
seen.add(row.absolute)
return true
})
}
function useDirectorySearch(args: {
@@ -237,6 +248,7 @@ function useDirectorySearch(args: {
export function DialogSelectDirectory(props: DialogSelectDirectoryProps) {
const sync = useGlobalSync()
const sdk = useGlobalSDK()
const layout = useLayout()
const dialog = useDialog()
const language = useLanguage()
@@ -266,9 +278,42 @@ export function DialogSelectDirectory(props: DialogSelectDirectoryProps) {
start,
})
const recentProjects = createMemo(() => {
const projects = layout.projects.list()
const byProject = new Map<string, number>()
for (const project of projects) {
let at = 0
const dirs = [project.worktree, ...(project.sandboxes ?? [])]
for (const directory of dirs) {
const sessions = sync.child(directory, { bootstrap: false })[0].session
for (const session of sessions) {
if (session.time.archived) continue
const updated = session.time.updated ?? session.time.created
if (updated > at) at = updated
}
}
byProject.set(project.worktree, at)
}
return projects
.map((project, index) => ({ project, at: byProject.get(project.worktree) ?? 0, index }))
.sort((a, b) => b.at - a.at || a.index - b.index)
.slice(0, 5)
.map(({ project }) => {
const row = toRow(project.worktree, home(), "recent")
const name = project.name || getFilename(project.worktree)
return {
...row,
search: `${row.search}\n${name}`,
}
})
})
const items = async (value: string) => {
const results = await directories(value)
return results.map((absolute) => toRow(absolute, home()))
const directoryRows = results.map((absolute) => toRow(absolute, home(), "folders"))
return uniqueRows([...recentProjects(), ...directoryRows])
}
function resolve(absolute: string) {
@@ -285,6 +330,14 @@ export function DialogSelectDirectory(props: DialogSelectDirectoryProps) {
items={items}
key={(x) => x.absolute}
filterKeys={["search"]}
groupBy={(item) => item.group}
sortGroupsBy={(a, b) => {
if (a.category === b.category) return 0
return a.category === "recent" ? -1 : 1
}}
groupHeader={(group) =>
group.category === "recent" ? language.t("home.recentProjects") : language.t("command.project.open")
}
ref={(r) => (list = r)}
onFilter={(value) => setFilter(cleanInput(value))}
onKeyEvent={(e, item) => {

View File

@@ -449,7 +449,7 @@ export function DialogSelectFile(props: { mode?: DialogSelectFileMode; onOpenFil
</div>
<Show when={item.updated}>
<span class="text-12-regular text-text-weak whitespace-nowrap ml-2">
{getRelativeTime(new Date(item.updated!).toISOString())}
{getRelativeTime(new Date(item.updated!).toISOString(), language.t)}
</span>
</Show>
</div>

View File

@@ -1310,43 +1310,45 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
</div>
</div>
<Show when={store.mode === "normal" && permission.permissionsEnabled() && params.id}>
<div class="pointer-events-none absolute bottom-2 left-2">
<div class="pointer-events-auto">
<TooltipKeybind
placement="top"
gutter={8}
title={language.t(
accepting() ? "command.permissions.autoaccept.disable" : "command.permissions.autoaccept.enable",
)}
keybind={command.keybind("permissions.autoaccept")}
<div class="pointer-events-none absolute bottom-2 left-2">
<div class="pointer-events-auto">
<TooltipKeybind
placement="top"
gutter={8}
title={language.t(
accepting() ? "command.permissions.autoaccept.disable" : "command.permissions.autoaccept.enable",
)}
keybind={command.keybind("permissions.autoaccept")}
>
<Button
data-action="prompt-permissions"
variant="ghost"
disabled={!params.id}
onClick={() => {
if (!params.id) return
permission.toggleAutoAccept(params.id, sdk.directory)
}}
classList={{
"size-6 flex items-center justify-center": true,
"text-text-base": !accepting(),
"hover:bg-surface-success-base": accepting(),
}}
aria-label={
accepting()
? language.t("command.permissions.autoaccept.disable")
: language.t("command.permissions.autoaccept.enable")
}
aria-pressed={accepting()}
>
<Button
data-action="prompt-permissions"
variant="ghost"
onClick={() => permission.toggleAutoAccept(params.id!, sdk.directory)}
classList={{
"_hidden group-hover/prompt-input:flex size-6 items-center justify-center": true,
"text-text-base": !accepting(),
"hover:bg-surface-success-base": accepting(),
}}
aria-label={
accepting()
? language.t("command.permissions.autoaccept.disable")
: language.t("command.permissions.autoaccept.enable")
}
aria-pressed={accepting()}
>
<Icon
name="chevron-double-right"
size="small"
classList={{ "text-icon-success-base": accepting() }}
/>
</Button>
</TooltipKeybind>
</div>
<Icon
name="chevron-double-right"
size="small"
classList={{ "text-icon-success-base": accepting() }}
/>
</Button>
</TooltipKeybind>
</div>
</Show>
</div>
</div>
</DockShellForm>
<Show when={store.mode === "normal" || store.mode === "shell"}>

View File

@@ -35,6 +35,7 @@ const OPEN_APPS = [
"terminal",
"iterm2",
"ghostty",
"warp",
"xcode",
"android-studio",
"powershell",
@@ -63,6 +64,7 @@ const MAC_APPS = [
{ id: "terminal", label: "Terminal", icon: "terminal", openWith: "Terminal" },
{ id: "iterm2", label: "iTerm2", icon: "iterm2", openWith: "iTerm" },
{ id: "ghostty", label: "Ghostty", icon: "ghostty", openWith: "Ghostty" },
{ id: "warp", label: "Warp", icon: "warp", openWith: "Warp" },
{ id: "xcode", label: "Xcode", icon: "xcode", openWith: "Xcode" },
{
id: "android-studio",
@@ -428,7 +430,7 @@ export function SessionHeader() {
<Spinner class="size-3.5 text-icon-base" />
</Show>
</div>
<span class="text-12-regular text-text-strong">Open</span>
<span class="text-12-regular text-text-strong">{language.t("common.open")}</span>
</Button>
<div class="self-stretch w-px bg-border-weak-base" />
<DropdownMenu

View File

@@ -162,7 +162,7 @@ export const SettingsProviders: Component = () => {
when={canDisconnect(item)}
fallback={
<span class="text-14-regular text-text-base opacity-0 group-hover:opacity-100 transition-opacity duration-200 pr-3 cursor-default">
Connected from your environment variables
{language.t("settings.providers.connected.environmentDescription")}
</span>
}
>
@@ -229,10 +229,12 @@ export const SettingsProviders: Component = () => {
<div class="flex flex-col min-w-0">
<div class="flex flex-wrap items-center gap-x-3 gap-y-1">
<ProviderIcon id={icon("synthetic")} class="size-5 shrink-0 icon-strong-base" />
<span class="text-14-medium text-text-strong">Custom provider</span>
<span class="text-14-medium text-text-strong">{language.t("provider.custom.title")}</span>
<Tag>{language.t("settings.providers.tag.custom")}</Tag>
</div>
<span class="text-12-regular text-text-weak pl-8">Add an OpenAI-compatible provider by base URL.</span>
<span class="text-12-regular text-text-weak pl-8">
{language.t("settings.providers.custom.description")}
</span>
</div>
<Button
size="large"

View File

@@ -204,7 +204,10 @@ function createGlobalSync() {
showToast({
variant: "error",
title: language.t("toast.session.listFailed.title", { project }),
description: formatServerError(err),
description: formatServerError(err, {
unknown: language.t("error.chain.unknown"),
invalidConfiguration: language.t("error.server.invalidConfiguration"),
}),
})
})
@@ -234,6 +237,8 @@ function createGlobalSync() {
setStore: child[1],
vcsCache: cache,
loadSessions,
unknownError: language.t("error.chain.unknown"),
invalidConfigurationError: language.t("error.server.invalidConfiguration"),
})
})()
@@ -308,6 +313,9 @@ function createGlobalSync() {
url: globalSDK.url,
}),
requestFailedTitle: language.t("common.requestFailed"),
unknownError: language.t("error.chain.unknown"),
invalidConfigurationError: language.t("error.server.invalidConfiguration"),
formatMoreCount: (count) => language.t("common.moreCountSuffix", { count }),
setGlobalStore,
})
}

View File

@@ -36,6 +36,9 @@ export async function bootstrapGlobal(input: {
connectErrorTitle: string
connectErrorDescription: string
requestFailedTitle: string
unknownError: string
invalidConfigurationError: string
formatMoreCount: (count: number) => string
setGlobalStore: SetStoreFunction<GlobalStore>
}) {
const health = await input.globalSDK.global
@@ -88,8 +91,11 @@ export async function bootstrapGlobal(input: {
const results = await Promise.allSettled(tasks)
const errors = results.filter((r): r is PromiseRejectedResult => r.status === "rejected").map((r) => r.reason)
if (errors.length) {
const message = errors[0] instanceof Error ? errors[0].message : String(errors[0])
const more = errors.length > 1 ? ` (+${errors.length - 1} more)` : ""
const message = formatServerError(errors[0], {
unknown: input.unknownError,
invalidConfiguration: input.invalidConfigurationError,
})
const more = errors.length > 1 ? input.formatMoreCount(errors.length - 1) : ""
showToast({
variant: "error",
title: input.requestFailedTitle,
@@ -116,6 +122,8 @@ export async function bootstrapDirectory(input: {
setStore: SetStoreFunction<State>
vcsCache: VcsCache
loadSessions: (directory: string) => Promise<void> | void
unknownError: string
invalidConfigurationError: string
}) {
if (input.store.status !== "complete") input.setStore("status", "loading")
@@ -137,7 +145,10 @@ export async function bootstrapDirectory(input: {
showToast({
variant: "error",
title: `Failed to reload ${project}`,
description: formatServerError(err),
description: formatServerError(err, {
unknown: input.unknownError,
invalidConfiguration: input.invalidConfigurationError,
}),
})
input.setStore("status", "partial")
return

View File

@@ -19,6 +19,7 @@ import { dict as no } from "@/i18n/no"
import { dict as br } from "@/i18n/br"
import { dict as th } from "@/i18n/th"
import { dict as bs } from "@/i18n/bs"
import { dict as tr } from "@/i18n/tr"
import { dict as uiEn } from "@opencode-ai/ui/i18n/en"
import { dict as uiZh } from "@opencode-ai/ui/i18n/zh"
import { dict as uiZht } from "@opencode-ai/ui/i18n/zht"
@@ -35,6 +36,7 @@ import { dict as uiNo } from "@opencode-ai/ui/i18n/no"
import { dict as uiBr } from "@opencode-ai/ui/i18n/br"
import { dict as uiTh } from "@opencode-ai/ui/i18n/th"
import { dict as uiBs } from "@opencode-ai/ui/i18n/bs"
import { dict as uiTr } from "@opencode-ai/ui/i18n/tr"
export type Locale =
| "en"
@@ -53,6 +55,7 @@ export type Locale =
| "br"
| "th"
| "bs"
| "tr"
type RawDictionary = typeof en & typeof uiEn
type Dictionary = i18n.Flatten<RawDictionary>
@@ -78,6 +81,7 @@ const LOCALES: readonly Locale[] = [
"no",
"br",
"th",
"tr",
]
const LABEL_KEY: Record<Locale, keyof Dictionary> = {
@@ -97,6 +101,7 @@ const LABEL_KEY: Record<Locale, keyof Dictionary> = {
br: "language.br",
th: "language.th",
bs: "language.bs",
tr: "language.tr",
}
const base = i18n.flatten({ ...en, ...uiEn })
@@ -117,6 +122,7 @@ const DICT: Record<Locale, Dictionary> = {
br: { ...base, ...i18n.flatten({ ...br, ...uiBr }) },
th: { ...base, ...i18n.flatten({ ...th, ...uiTh }) },
bs: { ...base, ...i18n.flatten({ ...bs, ...uiBs }) },
tr: { ...base, ...i18n.flatten({ ...tr, ...uiTr }) },
}
const localeMatchers: Array<{ locale: Locale; match: (language: string) => boolean }> = [
@@ -138,6 +144,7 @@ const localeMatchers: Array<{ locale: Locale; match: (language: string) => boole
{ locale: "br", match: (language) => language.startsWith("pt") },
{ locale: "th", match: (language) => language.startsWith("th") },
{ locale: "bs", match: (language) => language.startsWith("bs") },
{ locale: "tr", match: (language) => language.startsWith("tr") },
]
type ParityKey = "command.session.previous.unseen" | "command.session.next.unseen"
@@ -157,6 +164,7 @@ const PARITY_CHECK: Record<Exclude<Locale, "en">, Record<ParityKey, string>> = {
br,
th,
bs,
tr,
}
void PARITY_CHECK

View File

@@ -31,12 +31,33 @@ describe("autoRespondsPermission", () => {
expect(autoRespondsPermission({ root: true }, sessions, permission("child"), "/tmp/project")).toBe(true)
})
test("ignores auto-accept from unrelated sessions", () => {
test("defaults to auto-accept when no lineage override exists", () => {
const sessions = [session({ id: "root" }), session({ id: "child", parentID: "root" }), session({ id: "other" })]
const autoAccept = {
other: true,
}
expect(autoRespondsPermission(autoAccept, sessions, permission("child"), "/tmp/project")).toBe(false)
expect(autoRespondsPermission(autoAccept, sessions, permission("child"), "/tmp/project")).toBe(true)
})
test("inherits a parent session's false override", () => {
const directory = "/tmp/project"
const sessions = [session({ id: "root" }), session({ id: "child", parentID: "root" })]
const autoAccept = {
[`${base64Encode(directory)}/root`]: false,
}
expect(autoRespondsPermission(autoAccept, sessions, permission("child"), directory)).toBe(false)
})
test("prefers a child override over parent override", () => {
const directory = "/tmp/project"
const sessions = [session({ id: "root" }), session({ id: "child", parentID: "root" })]
const autoAccept = {
[`${base64Encode(directory)}/root`]: false,
[`${base64Encode(directory)}/child`]: true,
}
expect(autoRespondsPermission(autoAccept, sessions, permission("child"), directory)).toBe(true)
})
})

View File

@@ -5,6 +5,11 @@ export function acceptKey(sessionID: string, directory?: string) {
return `${base64Encode(directory)}/${sessionID}`
}
function accepted(autoAccept: Record<string, boolean>, sessionID: string, directory?: string) {
const key = acceptKey(sessionID, directory)
return autoAccept[key] ?? autoAccept[sessionID]
}
function sessionLineage(session: { id: string; parentID?: string }[], sessionID: string) {
const parent = session.reduce((acc, item) => {
if (item.parentID) acc.set(item.id, item.parentID)
@@ -29,8 +34,8 @@ export function autoRespondsPermission(
permission: { sessionID: string },
directory?: string,
) {
return sessionLineage(session, permission.sessionID).some((id) => {
const key = acceptKey(id, directory)
return autoAccept[key] ?? autoAccept[id] ?? false
})
const value = sessionLineage(session, permission.sessionID)
.map((id) => accepted(autoAccept, id, directory))
.find((item): item is boolean => item !== undefined)
return value ?? true
}

View File

@@ -115,8 +115,8 @@ export const { use: usePermission, provider: PermissionProvider } = createSimple
}
function isAutoAccepting(sessionID: string, directory?: string) {
const key = acceptKey(sessionID, directory)
return store.autoAccept[key] ?? store.autoAccept[sessionID] ?? false
const session = directory ? globalSync.child(directory, { bootstrap: false })[0].session : []
return autoRespondsPermission(store.autoAccept, session, { sessionID }, directory)
}
function shouldAutoRespond(permission: PermissionRequest, directory?: string) {
@@ -168,10 +168,11 @@ export const { use: usePermission, provider: PermissionProvider } = createSimple
function disable(sessionID: string, directory?: string) {
bumpEnableVersion(sessionID, directory)
const key = directory ? acceptKey(sessionID, directory) : undefined
const key = directory ? acceptKey(sessionID, directory) : sessionID
setStore(
produce((draft) => {
if (key) delete draft.autoAccept[key]
draft.autoAccept[key] = false
if (!directory) return
delete draft.autoAccept[sessionID]
}),
)

View File

@@ -734,4 +734,18 @@ export const dict = {
"workspace.reset.archived.one": "ستتم أرشفة جلسة واحدة.",
"workspace.reset.archived.many": "ستتم أرشفة {{count}} جلسات.",
"workspace.reset.note": "سيؤدي هذا إلى إعادة تعيين مساحة العمل لتتطابق مع الفرع الافتراضي.",
"common.open": "فتح",
"dialog.releaseNotes.action.getStarted": "البدء",
"dialog.releaseNotes.action.next": "التالي",
"dialog.releaseNotes.action.hideFuture": "عدم إظهار هذا في المستقبل",
"dialog.releaseNotes.media.alt": "معاينة الإصدار",
"toast.project.reloadFailed.title": "فشل في إعادة تحميل {{project}}",
"error.server.invalidConfiguration": "تكوين غير صالح",
"common.moreCountSuffix": " (+{{count}} إضافي)",
"common.time.justNow": "الآن",
"common.time.minutesAgo.short": "قبل {{count}} د",
"common.time.hoursAgo.short": "قبل {{count}} س",
"common.time.daysAgo.short": "قبل {{count}} ي",
"settings.providers.connected.environmentDescription": "متصل من متغيرات البيئة الخاصة بك",
"settings.providers.custom.description": "أضف مزود متوافق مع OpenAI بواسطة عنوان URL الأساسي.",
}

View File

@@ -742,4 +742,18 @@ export const dict = {
"workspace.reset.archived.one": "1 sessão será arquivada.",
"workspace.reset.archived.many": "{{count}} sessões serão arquivadas.",
"workspace.reset.note": "Isso redefinirá o espaço de trabalho para corresponder ao branch padrão.",
"common.open": "Abrir",
"dialog.releaseNotes.action.getStarted": "Começar",
"dialog.releaseNotes.action.next": "Próximo",
"dialog.releaseNotes.action.hideFuture": "Não mostrar isso no futuro",
"dialog.releaseNotes.media.alt": "Prévia do lançamento",
"toast.project.reloadFailed.title": "Falha ao recarregar {{project}}",
"error.server.invalidConfiguration": "Configuração inválida",
"common.moreCountSuffix": " (+{{count}} mais)",
"common.time.justNow": "Agora mesmo",
"common.time.minutesAgo.short": "{{count}}m atrás",
"common.time.hoursAgo.short": "{{count}}h atrás",
"common.time.daysAgo.short": "{{count}}d atrás",
"settings.providers.connected.environmentDescription": "Conectado a partir de suas variáveis de ambiente",
"settings.providers.custom.description": "Adicionar um provedor compatível com a OpenAI através do URL base.",
}

View File

@@ -819,4 +819,18 @@ export const dict = {
"workspace.reset.archived.one": "1 sesija će biti arhivirana.",
"workspace.reset.archived.many": "Biće arhivirano {{count}} sesija.",
"workspace.reset.note": "Ovo će resetovati radni prostor da odgovara podrazumijevanoj grani.",
"common.open": "Otvori",
"dialog.releaseNotes.action.getStarted": "Započni",
"dialog.releaseNotes.action.next": "Sljedeće",
"dialog.releaseNotes.action.hideFuture": "Ne prikazuj ovo u budućnosti",
"dialog.releaseNotes.media.alt": "Pregled izdanja",
"toast.project.reloadFailed.title": "Nije uspjelo ponovno učitavanje {{project}}",
"error.server.invalidConfiguration": "Nevažeća konfiguracija",
"common.moreCountSuffix": " (+{{count}} više)",
"common.time.justNow": "Upravo sada",
"common.time.minutesAgo.short": "prije {{count}} min",
"common.time.hoursAgo.short": "prije {{count}} h",
"common.time.daysAgo.short": "prije {{count}} d",
"settings.providers.connected.environmentDescription": "Povezano sa vašim varijablama okruženja",
"settings.providers.custom.description": "Dodajte provajdera kompatibilnog s OpenAI putem osnovnog URL-a.",
}

View File

@@ -813,4 +813,18 @@ export const dict = {
"workspace.reset.archived.one": "1 session vil blive arkiveret.",
"workspace.reset.archived.many": "{{count}} sessioner vil blive arkiveret.",
"workspace.reset.note": "Dette vil nulstille arbejdsområdet til at matche hovedgrenen.",
"common.open": "Åbn",
"dialog.releaseNotes.action.getStarted": "Kom i gang",
"dialog.releaseNotes.action.next": "Næste",
"dialog.releaseNotes.action.hideFuture": "Vis ikke disse i fremtiden",
"dialog.releaseNotes.media.alt": "Forhåndsvisning af udgivelse",
"toast.project.reloadFailed.title": "Kunne ikke genindlæse {{project}}",
"error.server.invalidConfiguration": "Ugyldig konfiguration",
"common.moreCountSuffix": " (+{{count}} mere)",
"common.time.justNow": "Lige nu",
"common.time.minutesAgo.short": "{{count}}m siden",
"common.time.hoursAgo.short": "{{count}}t siden",
"common.time.daysAgo.short": "{{count}}d siden",
"settings.providers.connected.environmentDescription": "Tilsluttet fra dine miljøvariabler",
"settings.providers.custom.description": "Tilføj en OpenAI-kompatibel udbyder via basis-URL.",
}

View File

@@ -751,4 +751,18 @@ export const dict = {
"workspace.reset.archived.one": "1 Sitzung wird archiviert.",
"workspace.reset.archived.many": "{{count}} Sitzungen werden archiviert.",
"workspace.reset.note": "Dadurch wird der Arbeitsbereich auf den Standard-Branch zurückgesetzt.",
"common.open": "Öffnen",
"dialog.releaseNotes.action.getStarted": "Loslegen",
"dialog.releaseNotes.action.next": "Weiter",
"dialog.releaseNotes.action.hideFuture": "In Zukunft nicht mehr anzeigen",
"dialog.releaseNotes.media.alt": "Vorschau auf die Version",
"toast.project.reloadFailed.title": "Fehler beim Neuladen von {{project}}",
"error.server.invalidConfiguration": "Ungültige Konfiguration",
"common.moreCountSuffix": " (+{{count}} weitere)",
"common.time.justNow": "Gerade eben",
"common.time.minutesAgo.short": "vor {{count}} Min",
"common.time.hoursAgo.short": "vor {{count}} Std",
"common.time.daysAgo.short": "vor {{count}} Tg",
"settings.providers.connected.environmentDescription": "Verbunden aus Ihren Umgebungsvariablen",
"settings.providers.custom.description": "Fügen Sie einen OpenAI-kompatiblen Anbieter per Basis-URL hinzu.",
} satisfies Partial<Record<Keys, string>>

View File

@@ -218,6 +218,7 @@ export const dict = {
"common.loading": "Loading",
"common.loading.ellipsis": "...",
"common.cancel": "Cancel",
"common.open": "Open",
"common.connect": "Connect",
"common.disconnect": "Disconnect",
"common.submit": "Submit",
@@ -347,6 +348,11 @@ export const dict = {
"dialog.project.edit.worktree.startup.description": "Runs after creating a new workspace (worktree).",
"dialog.project.edit.worktree.startup.placeholder": "e.g. bun install",
"dialog.releaseNotes.action.getStarted": "Get started",
"dialog.releaseNotes.action.next": "Next",
"dialog.releaseNotes.action.hideFuture": "Don't show these in the future",
"dialog.releaseNotes.media.alt": "Release preview",
"context.breakdown.title": "Context Breakdown",
"context.breakdown.note": 'Approximate breakdown of input tokens. "Other" includes tool definitions and overhead.',
"context.breakdown.system": "System",
@@ -397,6 +403,7 @@ export const dict = {
"language.br": "Português (Brasil)",
"language.bs": "Bosanski",
"language.th": "ไทย",
"language.tr": "Türkçe",
"toast.language.title": "Language",
"toast.language.description": "Switched to {{language}}",
@@ -435,6 +442,7 @@ export const dict = {
"toast.session.unshare.failed.description": "An error occurred while unsharing the session",
"toast.session.listFailed.title": "Failed to load sessions for {{project}}",
"toast.project.reloadFailed.title": "Failed to reload {{project}}",
"toast.update.title": "Update available",
"toast.update.description": "A new version of OpenCode ({{version}}) is now available to install.",
@@ -459,6 +467,7 @@ export const dict = {
"directory.error.invalidUrl": "Invalid directory in URL.",
"error.chain.unknown": "Unknown error",
"error.server.invalidConfiguration": "Invalid configuration",
"error.chain.causedBy": "Caused by:",
"error.chain.apiError": "API error",
"error.chain.status": "Status: {{status}}",
@@ -569,6 +578,7 @@ export const dict = {
"common.closeTab": "Close tab",
"common.dismiss": "Dismiss",
"common.moreCountSuffix": " (+{{count}} more)",
"common.requestFailed": "Request failed",
"common.moreOptions": "More options",
"common.learnMore": "Learn more",
@@ -581,6 +591,11 @@ export const dict = {
"common.loadMore": "Load more",
"common.key.esc": "ESC",
"common.time.justNow": "Just now",
"common.time.minutesAgo.short": "{{count}}m ago",
"common.time.hoursAgo.short": "{{count}}h ago",
"common.time.daysAgo.short": "{{count}}d ago",
"sidebar.menu.toggle": "Toggle menu",
"sidebar.nav.projectsAndSessions": "Projects and sessions",
"sidebar.settings": "Settings",
@@ -741,7 +756,9 @@ export const dict = {
"settings.providers.description": "Provider settings will be configurable here.",
"settings.providers.section.connected": "Connected providers",
"settings.providers.connected.empty": "No connected providers",
"settings.providers.connected.environmentDescription": "Connected from your environment variables",
"settings.providers.section.popular": "Popular providers",
"settings.providers.custom.description": "Add an OpenAI-compatible provider by base URL.",
"settings.providers.tag.environment": "Environment",
"settings.providers.tag.config": "Config",
"settings.providers.tag.custom": "Custom",

View File

@@ -825,4 +825,18 @@ export const dict = {
"workspace.reset.archived.one": "1 sesión será archivada.",
"workspace.reset.archived.many": "{{count}} sesiones serán archivadas.",
"workspace.reset.note": "Esto restablecerá el espacio de trabajo para coincidir con la rama predeterminada.",
"common.open": "Abrir",
"dialog.releaseNotes.action.getStarted": "Comenzar",
"dialog.releaseNotes.action.next": "Siguiente",
"dialog.releaseNotes.action.hideFuture": "No mostrar esto en el futuro",
"dialog.releaseNotes.media.alt": "Vista previa de la versión",
"toast.project.reloadFailed.title": "Error al recargar {{project}}",
"error.server.invalidConfiguration": "Configuración inválida",
"common.moreCountSuffix": " (+{{count}} más)",
"common.time.justNow": "Justo ahora",
"common.time.minutesAgo.short": "hace {{count}} min",
"common.time.hoursAgo.short": "hace {{count}} h",
"common.time.daysAgo.short": "hace {{count}} d",
"settings.providers.connected.environmentDescription": "Conectado desde tus variables de entorno",
"settings.providers.custom.description": "Añade un proveedor compatible con OpenAI por su URL base.",
}

View File

@@ -749,4 +749,18 @@ export const dict = {
"workspace.reset.archived.one": "1 session sera archivée.",
"workspace.reset.archived.many": "{{count}} sessions seront archivées.",
"workspace.reset.note": "Cela réinitialisera l'espace de travail pour correspondre à la branche par défaut.",
"common.open": "Ouvrir",
"dialog.releaseNotes.action.getStarted": "Commencer",
"dialog.releaseNotes.action.next": "Suivant",
"dialog.releaseNotes.action.hideFuture": "Ne plus afficher à l'avenir",
"dialog.releaseNotes.media.alt": "Aperçu de la version",
"toast.project.reloadFailed.title": "Échec du rechargement de {{project}}",
"error.server.invalidConfiguration": "Configuration invalide",
"common.moreCountSuffix": " (+{{count}} de plus)",
"common.time.justNow": "À l'instant",
"common.time.minutesAgo.short": "il y a {{count}}m",
"common.time.hoursAgo.short": "il y a {{count}}h",
"common.time.daysAgo.short": "il y a {{count}}j",
"settings.providers.connected.environmentDescription": "Connecté à partir de vos variables d'environnement",
"settings.providers.custom.description": "Ajouter un fournisseur compatible avec OpenAI via l'URL de base.",
}

View File

@@ -738,4 +738,18 @@ export const dict = {
"workspace.reset.archived.one": "1つのセッションがアーカイブされます。",
"workspace.reset.archived.many": "{{count}}個のセッションがアーカイブされます。",
"workspace.reset.note": "これにより、ワークスペースはデフォルトブランチと一致するようにリセットされます。",
"common.open": "開く",
"dialog.releaseNotes.action.getStarted": "始める",
"dialog.releaseNotes.action.next": "次へ",
"dialog.releaseNotes.action.hideFuture": "今後表示しない",
"dialog.releaseNotes.media.alt": "リリースのプレビュー",
"toast.project.reloadFailed.title": "{{project}} の再読み込みに失敗しました",
"error.server.invalidConfiguration": "無効な設定",
"common.moreCountSuffix": " (他 {{count}} 件)",
"common.time.justNow": "たった今",
"common.time.minutesAgo.short": "{{count}} 分前",
"common.time.hoursAgo.short": "{{count}} 時間前",
"common.time.daysAgo.short": "{{count}} 日前",
"settings.providers.connected.environmentDescription": "環境変数から接続されました",
"settings.providers.custom.description": "ベース URL を指定して OpenAI 互換のプロバイダーを追加します。",
}

View File

@@ -738,4 +738,18 @@ export const dict = {
"workspace.reset.archived.one": "1개의 세션이 보관됩니다.",
"workspace.reset.archived.many": "{{count}}개의 세션이 보관됩니다.",
"workspace.reset.note": "이 작업은 작업 공간을 기본 브랜치와 일치하도록 재설정합니다.",
"common.open": "열기",
"dialog.releaseNotes.action.getStarted": "시작하기",
"dialog.releaseNotes.action.next": "다음",
"dialog.releaseNotes.action.hideFuture": "다시 보지 않기",
"dialog.releaseNotes.media.alt": "릴리스 미리보기",
"toast.project.reloadFailed.title": "{{project}} 다시 불러오기 실패",
"error.server.invalidConfiguration": "잘못된 구성",
"common.moreCountSuffix": " (외 {{count}}개)",
"common.time.justNow": "방금 전",
"common.time.minutesAgo.short": "{{count}}분 전",
"common.time.hoursAgo.short": "{{count}}시간 전",
"common.time.daysAgo.short": "{{count}}일 전",
"settings.providers.connected.environmentDescription": "환경 변수에서 연결됨",
"settings.providers.custom.description": "기본 URL로 OpenAI 호환 공급자를 추가합니다.",
}

View File

@@ -821,4 +821,18 @@ export const dict = {
"workspace.reset.archived.one": "1 sesjon vil bli arkivert.",
"workspace.reset.archived.many": "{{count}} sesjoner vil bli arkivert.",
"workspace.reset.note": "Dette vil tilbakestille arbeidsområdet til å samsvare med standardgrenen.",
"common.open": "Åpne",
"dialog.releaseNotes.action.getStarted": "Kom i gang",
"dialog.releaseNotes.action.next": "Neste",
"dialog.releaseNotes.action.hideFuture": "Ikke vis disse igjen",
"dialog.releaseNotes.media.alt": "Forhåndsvisning av utgivelse",
"toast.project.reloadFailed.title": "Kunne ikke laste inn {{project}} på nytt",
"error.server.invalidConfiguration": "Ugyldig konfigurasjon",
"common.moreCountSuffix": " (+{{count}} mer)",
"common.time.justNow": "Akkurat nå",
"common.time.minutesAgo.short": "{{count}} m siden",
"common.time.hoursAgo.short": "{{count}} t siden",
"common.time.daysAgo.short": "{{count}} d siden",
"settings.providers.connected.environmentDescription": "Koblet til fra miljøvariablene dine",
"settings.providers.custom.description": "Legg til en OpenAI-kompatibel leverandør via basis-URL.",
} satisfies Partial<Record<Keys, string>>

View File

@@ -15,8 +15,9 @@ import { dict as ru } from "./ru"
import { dict as th } from "./th"
import { dict as zh } from "./zh"
import { dict as zht } from "./zht"
import { dict as tr } from "./tr"
const locales = [ar, br, bs, da, de, es, fr, ja, ko, no, pl, ru, th, zh, zht]
const locales = [ar, br, bs, da, de, es, fr, ja, ko, no, pl, ru, th, tr, zh, zht]
const keys = ["command.session.previous.unseen", "command.session.next.unseen"] as const
describe("i18n parity", () => {

View File

@@ -740,4 +740,18 @@ export const dict = {
"workspace.reset.archived.one": "1 sesja zostanie zarchiwizowana.",
"workspace.reset.archived.many": "{{count}} sesji zostanie zarchiwizowanych.",
"workspace.reset.note": "To zresetuje przestrzeń roboczą, aby odpowiadała domyślnej gałęzi.",
"common.open": "Otwórz",
"dialog.releaseNotes.action.getStarted": "Rozpocznij",
"dialog.releaseNotes.action.next": "Dalej",
"dialog.releaseNotes.action.hideFuture": "Nie pokazuj tego w przyszłości",
"dialog.releaseNotes.media.alt": "Podgląd wydania",
"toast.project.reloadFailed.title": "Nie udało się ponownie wczytać {{project}}",
"error.server.invalidConfiguration": "Nieprawidłowa konfiguracja",
"common.moreCountSuffix": " (jeszcze {{count}})",
"common.time.justNow": "Przed chwilą",
"common.time.minutesAgo.short": "{{count}} min temu",
"common.time.hoursAgo.short": "{{count}} godz. temu",
"common.time.daysAgo.short": "{{count}} dni temu",
"settings.providers.connected.environmentDescription": "Połączono ze zmiennymi środowiskowymi",
"settings.providers.custom.description": "Dodaj dostawcę zgodnego z OpenAI poprzez podstawowy URL.",
}

View File

@@ -821,4 +821,18 @@ export const dict = {
"workspace.reset.archived.one": "1 сессия будет архивирована.",
"workspace.reset.archived.many": "{{count}} сессий будет архивировано.",
"workspace.reset.note": "Рабочее пространство будет сброшено в соответствие с веткой по умолчанию.",
"common.open": "Открыть",
"dialog.releaseNotes.action.getStarted": "Начать",
"dialog.releaseNotes.action.next": "Далее",
"dialog.releaseNotes.action.hideFuture": "Больше не показывать",
"dialog.releaseNotes.media.alt": "Превью релиза",
"toast.project.reloadFailed.title": "Не удалось перезагрузить {{project}}",
"error.server.invalidConfiguration": "Недопустимая конфигурация",
"common.moreCountSuffix": " (ещё {{count}})",
"common.time.justNow": "Только что",
"common.time.minutesAgo.short": "{{count}} мин назад",
"common.time.hoursAgo.short": "{{count}} ч назад",
"common.time.daysAgo.short": "{{count}} д назад",
"settings.providers.connected.environmentDescription": "Подключено из ваших переменных окружения",
"settings.providers.custom.description": "Добавить провайдера, совместимого с OpenAI, по базовому URL.",
}

View File

@@ -811,4 +811,18 @@ export const dict = {
"workspace.reset.archived.one": "1 เซสชันจะถูกจัดเก็บ",
"workspace.reset.archived.many": "{{count}} เซสชันจะถูกจัดเก็บ",
"workspace.reset.note": "สิ่งนี้จะรีเซ็ตพื้นที่ทำงานให้ตรงกับสาขาเริ่มต้น",
"common.open": "เปิด",
"dialog.releaseNotes.action.getStarted": "เริ่มต้น",
"dialog.releaseNotes.action.next": "ถัดไป",
"dialog.releaseNotes.action.hideFuture": "ไม่ต้องแสดงสิ่งนี้อีกในอนาคต",
"dialog.releaseNotes.media.alt": "ตัวอย่างรุ่น",
"toast.project.reloadFailed.title": "ไม่สามารถโหลด {{project}} ใหม่ได้",
"error.server.invalidConfiguration": "การกำหนดค่าไม่ถูกต้อง",
"common.moreCountSuffix": " (เพิ่มอีก {{count}})",
"common.time.justNow": "เมื่อสักครู่นี้",
"common.time.minutesAgo.short": "{{count}} นาทีที่แล้ว",
"common.time.hoursAgo.short": "{{count}} ชม. ที่แล้ว",
"common.time.daysAgo.short": "{{count}} วันที่แล้ว",
"settings.providers.connected.environmentDescription": "เชื่อมต่อจากตัวแปรสภาพแวดล้อมของคุณ",
"settings.providers.custom.description": "เพิ่มผู้ให้บริการที่รองรับ OpenAI ด้วย URL หลัก",
}

849
packages/app/src/i18n/tr.ts Normal file
View File

@@ -0,0 +1,849 @@
import { dict as en } from "./en"
type Keys = keyof typeof en
export const dict = {
"command.category.suggested": "Önerilen",
"command.category.view": "Görünüm",
"command.category.project": "Proje",
"command.category.provider": "Sağlayıcı",
"command.category.server": "Sunucu",
"command.category.session": "Oturum",
"command.category.theme": "Tema",
"command.category.language": "Dil",
"command.category.file": "Dosya",
"command.category.context": "Bağlam",
"command.category.terminal": "Terminal",
"command.category.model": "Model",
"command.category.mcp": "MCP",
"command.category.agent": "Ajan",
"command.category.permissions": "İzinler",
"command.category.workspace": "Çalışma Alanı",
"command.category.settings": "Ayarlar",
"theme.scheme.system": "Sistem",
"theme.scheme.light": "Açık",
"theme.scheme.dark": "Koyu",
"command.sidebar.toggle": "Kenar çubuğunu aç/kapat",
"command.project.open": "Proje aç",
"command.provider.connect": "Sağlayıcı bağla",
"command.server.switch": "Sunucu değiştir",
"command.settings.open": "Ayarları aç",
"command.session.previous": "Önceki oturum",
"command.session.next": "Sonraki oturum",
"command.session.previous.unseen": "Önceki okunmamış oturum",
"command.session.next.unseen": "Sonraki okunmamış oturum",
"command.session.archive": "Oturumu arşivle",
"command.palette": "Komut paleti",
"command.theme.cycle": "Tema değiştir",
"command.theme.set": "Tema kullan: {{theme}}",
"command.theme.scheme.cycle": "Renk şemasını değiştir",
"command.theme.scheme.set": "Renk şeması kullan: {{scheme}}",
"command.language.cycle": "Dil değiştir",
"command.language.set": "Dil kullan: {{language}}",
"command.session.new": "Yeni oturum",
"command.file.open": "Dosya aç",
"command.tab.close": "Sekmeyi kapat",
"command.context.addSelection": "Seçimi bağlama ekle",
"command.context.addSelection.description": "Mevcut dosyadan seçili satırları ekle",
"command.input.focus": "Girişi odakla",
"command.terminal.toggle": "Terminali aç/kapat",
"command.fileTree.toggle": "Dosya ağacını aç/kapat",
"command.review.toggle": "İncelemeyi aç/kapat",
"command.terminal.new": "Yeni terminal",
"command.terminal.new.description": "Yeni bir terminal sekmesi oluştur",
"command.steps.toggle": "Adımları aç/kapat",
"command.steps.toggle.description": "Mevcut mesaj için adımları göster veya gizle",
"command.message.previous": "Önceki mesaj",
"command.message.previous.description": "Önceki kullanıcı mesajına git",
"command.message.next": "Sonraki mesaj",
"command.message.next.description": "Sonraki kullanıcı mesajına git",
"command.model.choose": "Model seç",
"command.model.choose.description": "Farklı bir model seç",
"command.mcp.toggle": "MCP'leri aç/kapat",
"command.mcp.toggle.description": "MCP'leri aç/kapat",
"command.agent.cycle": "Ajan değiştir",
"command.agent.cycle.description": "Sonraki ajana geç",
"command.agent.cycle.reverse": "Ajanı geri değiştir",
"command.agent.cycle.reverse.description": "Önceki ajana geç",
"command.model.variant.cycle": "Düşünme eforu değiştir",
"command.model.variant.cycle.description": "Sonraki efor seviyesine geç",
"command.prompt.mode.shell": "Kabuk",
"command.prompt.mode.normal": "Komut",
"command.permissions.autoaccept.enable": "Düzenlemeleri otomatik kabul et",
"command.permissions.autoaccept.disable": "Otomatik kabulü durdur",
"command.workspace.toggle": "Çalışma alanlarını aç/kapat",
"command.workspace.toggle.description": "Kenar çubuğunda birden fazla çalışma alanını göster veya gizle",
"command.session.undo": "Geri al",
"command.session.undo.description": "Son mesajı geri al",
"command.session.redo": "Yinele",
"command.session.redo.description": "Son geri alınan mesajı yinele",
"command.session.compact": "Oturumu sıkıştır",
"command.session.compact.description": "Bağlam boyutunu azaltmak için oturumu özetle",
"command.session.fork": "Mesajdan dallandır",
"command.session.fork.description": "Önceki bir mesajdan yeni oturum oluştur",
"command.session.share": "Oturumu paylaş",
"command.session.share.description": "Bu oturumu paylaş ve URL'yi panoya kopyala",
"command.session.unshare": "Paylaşımı kaldır",
"command.session.unshare.description": "Bu oturumun paylaşımını durdur",
"palette.search.placeholder": "Dosya, komut ve oturum ara",
"palette.empty": "Sonuç bulunamadı",
"palette.group.commands": "Komutlar",
"palette.group.files": "Dosyalar",
"dialog.provider.search.placeholder": "Sağlayıcı ara",
"dialog.provider.empty": "Sağlayıcı bulunamadı",
"dialog.provider.group.popular": "Popüler",
"dialog.provider.group.other": "Diğer",
"dialog.provider.tag.recommended": "Önerilen",
"dialog.provider.opencode.note": "Claude, GPT, Gemini ve daha fazlasını içeren seçilmiş modeller",
"dialog.provider.opencode.tagline": "Güvenilir optimize edilmiş modeller",
"dialog.provider.opencodeGo.tagline": "Herkes için düşük maliyetli abonelik",
"dialog.provider.anthropic.note": "Pro ve Max dahil Claude modellerine doğrudan erişim",
"dialog.provider.copilot.note": "GitHub Copilot üzerinden kodlama yardımı için yapay zekâ modelleri",
"dialog.provider.openai.note": "Hızlı ve yetenekli genel yapay zekâ görevleri için GPT modelleri",
"dialog.provider.google.note": "Hızlı ve yapılandırılmış yanıtlar için Gemini modelleri",
"dialog.provider.openrouter.note": "Tek bir sağlayıcıdan tüm desteklenen modellere eriş",
"dialog.provider.vercel.note": "Akıllı yönlendirme ile yapay zekâ modellerine birleşik erişim",
"dialog.model.select.title": "Model seç",
"dialog.model.search.placeholder": "Model ara",
"dialog.model.empty": "Model sonucu yok",
"dialog.model.manage": "Modelleri yönet",
"dialog.model.manage.description": "Model seçicide hangi modellerin görüneceğini özelleştirin.",
"dialog.model.manage.provider.toggle": "Tüm {{provider}} modellerini aç/kapat",
"dialog.model.unpaid.freeModels.title": "OpenCode tarafından sunulan ücretsiz modeller",
"dialog.model.unpaid.addMore.title": "Popüler sağlayıcılardan daha fazla model ekleyin",
"dialog.provider.viewAll": "Daha fazla sağlayıcı göster",
"provider.connect.title": "{{provider}} bağla",
"provider.connect.title.anthropicProMax": "Claude Pro/Max ile giriş yap",
"provider.connect.selectMethod": "{{provider}} için giriş yöntemini seçin.",
"provider.connect.method.apiKey": "API anahtarı",
"provider.connect.status.inProgress": "Yetkilendirme devam ediyor...",
"provider.connect.status.waiting": "Yetkilendirme bekleniyor...",
"provider.connect.status.failed": "Yetkilendirme başarısız: {{error}}",
"provider.connect.apiKey.description":
"{{provider}} hesabınızı bağlamak ve OpenCode'da {{provider}} modellerini kullanmak için {{provider}} API anahtarınızı girin.",
"provider.connect.apiKey.label": "{{provider}} API anahtarı",
"provider.connect.apiKey.placeholder": "API anahtarı",
"provider.connect.apiKey.required": "API anahtarı gerekli",
"provider.connect.opencodeZen.line1":
"OpenCode Zen, kodlama ajanları için seçilmiş güvenilir optimize edilmiş modellere erişim sağlar.",
"provider.connect.opencodeZen.line2":
"Tek bir API anahtarıyla Claude, GPT, Gemini, GLM ve daha fazlası gibi modellere erişebilirsiniz.",
"provider.connect.opencodeZen.visit.prefix": "",
"provider.connect.opencodeZen.visit.link": "opencode.ai/zen",
"provider.connect.opencodeZen.visit.suffix": " adresini ziyaret ederek API anahtarınızı alın.",
"provider.connect.oauth.code.visit.prefix":
"Hesabınızı bağlamak ve OpenCode'da {{provider}} modellerini kullanmak için ",
"provider.connect.oauth.code.visit.link": "bu bağlantıya",
"provider.connect.oauth.code.visit.suffix": " tıklayarak yetkilendirme kodunuzu alın.",
"provider.connect.oauth.code.label": "{{method}} yetkilendirme kodu",
"provider.connect.oauth.code.placeholder": "Yetkilendirme kodu",
"provider.connect.oauth.code.required": "Yetkilendirme kodu gerekli",
"provider.connect.oauth.code.invalid": "Geçersiz yetkilendirme kodu",
"provider.connect.oauth.auto.visit.prefix": "",
"provider.connect.oauth.auto.visit.link": "Bu bağlantıya",
"provider.connect.oauth.auto.visit.suffix":
" tıklayarak aşağıdaki kodu girin ve hesabınızı bağlayarak OpenCode'da {{provider}} modellerini kullanın.",
"provider.connect.oauth.auto.confirmationCode": "Onay kodu",
"provider.connect.toast.connected.title": "{{provider}} bağlandı",
"provider.connect.toast.connected.description": "{{provider}} modelleri artık kullanımda.",
"provider.custom.title": "Özel sağlayıcı",
"provider.custom.description.prefix": "OpenAI uyumlu bir sağlayıcı yapılandırın. ",
"provider.custom.description.link": "Sağlayıcı yapılandırma dökümanları",
"provider.custom.description.suffix": " sayfasına bakın.",
"provider.custom.field.providerID.label": "Sağlayıcı kimlik",
"provider.custom.field.providerID.placeholder": "saglayicim",
"provider.custom.field.providerID.description": "Küçük harfler, rakamlar, tire veya alt çizgi",
"provider.custom.field.name.label": "Görünen ad",
"provider.custom.field.name.placeholder": "Yapay Zekâ Sağlayıcım",
"provider.custom.field.baseURL.label": "Temel URL",
"provider.custom.field.baseURL.placeholder": "https://api.saglayicim.com/v1",
"provider.custom.field.apiKey.label": "API anahtarı",
"provider.custom.field.apiKey.placeholder": "API anahtarı",
"provider.custom.field.apiKey.description":
"İsteğe bağlı. Kimlik doğrulamayı başlıklar ile yönetiyorsanız boş bırakın.",
"provider.custom.models.label": "Modeller",
"provider.custom.models.id.label": "Kimlik",
"provider.custom.models.id.placeholder": "model-kimlik",
"provider.custom.models.name.label": "Ad",
"provider.custom.models.name.placeholder": "Görünen Ad",
"provider.custom.models.remove": "Modeli kaldır",
"provider.custom.models.add": "Model ekle",
"provider.custom.headers.label": "Başlıklar (isteğe bağlı)",
"provider.custom.headers.key.label": "Başlık",
"provider.custom.headers.key.placeholder": "Başlık-Adı",
"provider.custom.headers.value.label": "Değer",
"provider.custom.headers.value.placeholder": "değer",
"provider.custom.headers.remove": "Başlığı kaldır",
"provider.custom.headers.add": "Başlık ekle",
"provider.custom.error.providerID.required": "Sağlayıcı kimlik gerekli",
"provider.custom.error.providerID.format": "Küçük harf, rakam, tire veya alt çizgi kullanın",
"provider.custom.error.providerID.exists": "Bu sağlayıcı kimlik zaten mevcut",
"provider.custom.error.name.required": "Görünen ad gerekli",
"provider.custom.error.baseURL.required": "Temel URL gerekli",
"provider.custom.error.baseURL.format": "http:// veya https:// ile başlamalı",
"provider.custom.error.required": "Gerekli",
"provider.custom.error.duplicate": "Tekrar",
"provider.disconnect.toast.disconnected.title": "{{provider}} bağlantısı kesildi",
"provider.disconnect.toast.disconnected.description": "{{provider}} modelleri artık kullanılabilir değil.",
"model.tag.free": "Ücretsiz",
"model.tag.latest": "En yeni",
"model.provider.anthropic": "Anthropic",
"model.provider.openai": "OpenAI",
"model.provider.google": "Google",
"model.provider.xai": "xAI",
"model.provider.meta": "Meta",
"model.input.text": "metin",
"model.input.image": "görsel",
"model.input.audio": "ses",
"model.input.video": "video",
"model.input.pdf": "pdf",
"model.tooltip.allows": "Kabul eder: {{inputs}}",
"model.tooltip.reasoning.allowed": "Akıl yürütme destekler",
"model.tooltip.reasoning.none": "Akıl yürütme yok",
"model.tooltip.context": "Bağlam limiti {{limit}}",
"common.search.placeholder": "Ara",
"common.goBack": "Geri git",
"common.goForward": "İleri git",
"common.loading": "Yükleniyor",
"common.loading.ellipsis": "...",
"common.cancel": "İptal",
"common.connect": "Bağlan",
"common.disconnect": "Bağlantı Kes",
"common.submit": "Gönder",
"common.save": "Kaydet",
"common.saving": "Kaydediliyor...",
"common.default": "Varsayılan",
"common.attachment": "ek",
"prompt.placeholder.shell": "Kabuk komutu girin...",
"prompt.placeholder.normal": 'Bir şeyler sorun... "{{example}}"',
"prompt.placeholder.simple": "Bir şeyler sorun...",
"prompt.placeholder.summarizeComments": "Yorumları özetle…",
"prompt.placeholder.summarizeComment": "Yorumu özetle…",
"prompt.mode.shell": "Kabuk",
"prompt.mode.normal": "Komut",
"prompt.mode.shell.exit": ıkmak için esc",
"prompt.example.1": "Kod tabanındaki bir TODO'yu düzelt",
"prompt.example.2": "Bu projenin teknoloji yığını nedir?",
"prompt.example.3": "Bozuk testleri düzelt",
"prompt.example.4": "Kimlik doğrulamanın nasıl çalıştığınııkla",
"prompt.example.5": "Güvenlik açıkları bul ve düzelt",
"prompt.example.6": "Kullanıcı servisi için birim testleri ekle",
"prompt.example.7": "Bu fonksiyonu daha okunabilir hale getir",
"prompt.example.8": "Bu hata ne anlama geliyor?",
"prompt.example.9": "Bu sorunu ayıklamama yardım et",
"prompt.example.10": "API dokümantasyonu oluştur",
"prompt.example.11": "Veritabanı sorgularını optimize et",
"prompt.example.12": "Girdi doğrulama ekle",
"prompt.example.13": "İçin yeni bir bileşen oluştur...",
"prompt.example.14": "Bu projeyi nasıl dağıtabilirim?",
"prompt.example.15": "Kodumu en iyi uygulamalar için incele",
"prompt.example.16": "Bu fonksiyona hata yönetimi ekle",
"prompt.example.17": "Bu regex kalıbınııkla",
"prompt.example.18": "Bunu TypeScript'e dönüştür",
"prompt.example.19": "Kod tabanı boyunca loglama ekle",
"prompt.example.20": "Hangi bağımlılıklar güncellenmemiş?",
"prompt.example.21": "Bir göç betiği yazmama yardım et",
"prompt.example.22": "Bu uç nokta için önbellekleme uygula",
"prompt.example.23": "Bu listeye sayfalama ekle",
"prompt.example.24": "İçin bir CLI komutu oluştur...",
"prompt.example.25": "Ortam değişkenleri burada nasıl çalışıyor?",
"prompt.popover.emptyResults": "Eşleşen sonuç yok",
"prompt.popover.emptyCommands": "Eşleşen komut yok",
"prompt.dropzone.label": "Görsel veya PDF'leri buraya bırakın",
"prompt.dropzone.file.label": "@bahsetmek için dosyayı bırakın",
"prompt.slash.badge.custom": "özel",
"prompt.slash.badge.skill": "beceri",
"prompt.slash.badge.mcp": "mcp",
"prompt.context.active": "aktif",
"prompt.context.includeActiveFile": "Aktif dosyayı dahil et",
"prompt.context.removeActiveFile": "Aktif dosyayı bağlamdan çıkar",
"prompt.context.removeFile": "Dosyayı bağlamdan çıkar",
"prompt.action.attachFile": "Dosya ekle",
"prompt.attachment.remove": "Eki kaldır",
"prompt.action.send": "Gönder",
"prompt.action.stop": "Durdur",
"prompt.toast.pasteUnsupported.title": "Desteklenmeyen yapıştırma",
"prompt.toast.pasteUnsupported.description": "Buraya sadece görsel veya PDF yapıştırılabilir.",
"prompt.toast.modelAgentRequired.title": "Bir ajan ve model seçin",
"prompt.toast.modelAgentRequired.description": "Komut göndermeden önce bir ajan ve model seçin.",
"prompt.toast.worktreeCreateFailed.title": "Çalışma ağacı oluşturulamadı",
"prompt.toast.sessionCreateFailed.title": "Oturum oluşturulamadı",
"prompt.toast.shellSendFailed.title": "Kabuk komutu gönderilemedi",
"prompt.toast.commandSendFailed.title": "Komut gönderilemedi",
"prompt.toast.promptSendFailed.title": "Komut gönderilemedi",
"prompt.toast.promptSendFailed.description": "Oturum alınamadı",
"dialog.mcp.title": "MCP'ler",
"dialog.mcp.description": "{{total}} içerisinden {{enabled}} etkin",
"dialog.mcp.empty": "Yapılandırılmış MCP yok",
"dialog.lsp.empty": "LSP'ler dosya türlerinden otomatik algılanır",
"dialog.plugins.empty": "Eklentiler opencode.json içinde yapılandırılır",
"mcp.status.connected": "bağlı",
"mcp.status.failed": "başarısız",
"mcp.status.needs_auth": "kimlik doğrulama gerekli",
"mcp.status.disabled": "devre dışı",
"dialog.fork.empty": "Dallandırılacak mesaj yok",
"dialog.directory.search.placeholder": "Klasör ara",
"dialog.directory.empty": "Klasör bulunamadı",
"dialog.server.title": "Sunucular",
"dialog.server.description": "Bu uygulamanın hangi OpenCode sunucusuna bağlanacağını değiştirin.",
"dialog.server.search.placeholder": "Sunucu ara",
"dialog.server.empty": "Henüz sunucu yok",
"dialog.server.add.title": "Sunucu ekle",
"dialog.server.add.url": "Sunucu URL'si",
"dialog.server.add.placeholder": "http://localhost:4096",
"dialog.server.add.error": "Sunucuya bağlanılamadı",
"dialog.server.add.checking": "Kontrol ediliyor...",
"dialog.server.add.button": "Sunucu ekle",
"dialog.server.default.title": "Varsayılan sunucu",
"dialog.server.default.description":
"Uygulama başlatıldığında yerel sunucu başlatmak yerine bu sunucuya bağlan. Yeniden başlatma gerektirir.",
"dialog.server.default.none": "Sunucu seçilmedi",
"dialog.server.default.set": "Mevcut sunucuyu varsayılan olarak ayarla",
"dialog.server.default.clear": "Temizle",
"dialog.server.action.remove": "Sunucuyu kaldır",
"dialog.server.menu.edit": "Düzenle",
"dialog.server.menu.default": "Varsayılan olarak ayarla",
"dialog.server.menu.defaultRemove": "Varsayılanı kaldır",
"dialog.server.menu.delete": "Sil",
"dialog.server.current": "Mevcut Sunucu",
"dialog.server.status.default": "Varsayılan",
"dialog.project.edit.title": "Projeyi düzenle",
"dialog.project.edit.name": "Ad",
"dialog.project.edit.icon": "Simge",
"dialog.project.edit.icon.alt": "Proje simgesi",
"dialog.project.edit.icon.hint": "Tıkla veya bir görsel sürükle",
"dialog.project.edit.icon.recommended": "Önerilen: 128x128px",
"dialog.project.edit.color": "Renk",
"dialog.project.edit.color.select": "{{color}} rengini seç",
"dialog.project.edit.worktree.startup": "Çalışma alanı başlatma betiği",
"dialog.project.edit.worktree.startup.description": "Yeni bir çalışma alanı (worktree) oluşturduktan sonra çalışır.",
"dialog.project.edit.worktree.startup.placeholder": "örneğin bun install",
"context.breakdown.title": "Bağlam Dökümü",
"context.breakdown.note": 'Girdi tokenlerinin yaklaşık dökümü. "Diğer" araç tanımları ve ek yükleri içerir.',
"context.breakdown.system": "Sistem",
"context.breakdown.user": "Kullanıcı",
"context.breakdown.assistant": "Asistan",
"context.breakdown.tool": "Araç Çağrıları",
"context.breakdown.other": "Diğer",
"context.systemPrompt.title": "Sistem Komutu",
"context.rawMessages.title": "Ham mesajlar",
"context.stats.session": "Oturum",
"context.stats.messages": "Mesajlar",
"context.stats.provider": "Sağlayıcı",
"context.stats.model": "Model",
"context.stats.limit": "Bağlam Limiti",
"context.stats.totalTokens": "Toplam Token",
"context.stats.usage": "Kullanım",
"context.stats.inputTokens": "Girdi Tokenleri",
"context.stats.outputTokens": ıktı Tokenleri",
"context.stats.reasoningTokens": "Akıl Yürütme Tokenleri",
"context.stats.cacheTokens": "Önbellek Tokenleri (okuma/yazma)",
"context.stats.userMessages": "Kullanıcı Mesajları",
"context.stats.assistantMessages": "Asistan Mesajları",
"context.stats.totalCost": "Toplam Maliyet",
"context.stats.sessionCreated": "Oturum Oluşturulma",
"context.stats.lastActivity": "Son Etkinlik",
"context.usage.tokens": "Tokenler",
"context.usage.usage": "Kullanım",
"context.usage.cost": "Maliyet",
"context.usage.clickToView": "Bağlamı görüntüle",
"context.usage.view": "Bağlam kullanımını görüntüle",
"language.en": "English",
"language.zh": "简体中文",
"language.zht": "繁體中文",
"language.ko": "한국어",
"language.de": "Deutsch",
"language.es": "Español",
"language.fr": "Français",
"language.da": "Dansk",
"language.ja": "日本語",
"language.pl": "Polski",
"language.ru": "Русский",
"language.ar": "العربية",
"language.no": "Norsk",
"language.br": "Português (Brasil)",
"language.bs": "Bosanski",
"language.th": "ไทย",
"language.tr": "Türkçe",
"toast.language.title": "Dil",
"toast.language.description": "{{language}} diline geçildi",
"toast.theme.title": "Tema değiştirildi",
"toast.scheme.title": "Renk şeması",
"toast.workspace.enabled.title": "Çalışma alanları etkinleştirildi",
"toast.workspace.enabled.description": "Kenar çubuğunda birden fazla çalışma ağacı gösterilecek",
"toast.workspace.disabled.title": "Çalışma alanları devre dışı bırakıldı",
"toast.workspace.disabled.description": "Kenar çubuğunda yalnızca ana çalışma ağacı gösterilecek",
"toast.permissions.autoaccept.on.title": "Düzenlemeler otomatik kabul ediliyor",
"toast.permissions.autoaccept.on.description": "Düzenleme ve yazma izinleri otomatik olarak onaylanacak",
"toast.permissions.autoaccept.off.title": "Otomatik kabul durduruldu",
"toast.permissions.autoaccept.off.description": "Düzenleme ve yazma izinleri onay gerektirecek",
"toast.model.none.title": "Model seçilmedi",
"toast.model.none.description": "Bu oturumu özetlemek için bir sağlayıcı bağlayın",
"toast.file.loadFailed.title": "Dosya yüklenemedi",
"toast.file.listFailed.title": "Dosyalar listelenemedi",
"toast.context.noLineSelection.title": "Satır seçimi yok",
"toast.context.noLineSelection.description": "Önce bir dosya sekmesinde satır aralığı seçin.",
"toast.session.share.copyFailed.title": "URL panoya kopyalanamadı",
"toast.session.share.success.title": "Oturum paylaşıldı",
"toast.session.share.success.description": "Paylaşım URL'si panoya kopyalandı!",
"toast.session.share.failed.title": "Oturum paylaşılamadı",
"toast.session.share.failed.description": "Oturum paylaşılırken bir hata oluştu",
"toast.session.unshare.success.title": "Oturum paylaşımı kaldırıldı",
"toast.session.unshare.success.description": "Oturum paylaşımı başarıyla kaldırıldı!",
"toast.session.unshare.failed.title": "Oturum paylaşımı kaldırılamadı",
"toast.session.unshare.failed.description": "Oturum paylaşımı kaldırılırken bir hata oluştu",
"toast.session.listFailed.title": "{{project}} için oturumlar yüklenemedi",
"toast.update.title": "Güncelleme mevcut",
"toast.update.description": "OpenCode'un yeni bir sürümü ({{version}}) yüklemeye hazır.",
"toast.update.action.installRestart": "Yükle ve yeniden başlat",
"toast.update.action.notYet": "Şimdi değil",
"error.page.title": "Bir şeyler yanlış gitti",
"error.page.description": "Uygulama yüklenirken bir hata oluştu.",
"error.page.details.label": "Hata Detayları",
"error.page.action.restart": "Yeniden Başlat",
"error.page.action.checking": "Kontrol ediliyor...",
"error.page.action.checkUpdates": "Güncellemeleri kontrol et",
"error.page.action.updateTo": "{{version}} sürümüne güncelle",
"error.page.report.prefix": "Lütfen bu hatayı OpenCode ekibine bildirin",
"error.page.report.discord": "Discord üzerinden",
"error.page.version": "Sürüm: {{version}}",
"error.dev.rootNotFound":
"Kök eleman bulunamadı. index.html dosyanıza eklemeyi unuttunuz mu? Ya da id özelliği yanlış mı yazıldı?",
"error.globalSync.connectFailed": "Sunucuya bağlanılamadı. `{{url}}` adresinde çalışan bir sunucu var mı?",
"directory.error.invalidUrl": "URL'de geçersiz dizin.",
"error.chain.unknown": "Bilinmeyen hata",
"error.chain.causedBy": "Nedeni:",
"error.chain.apiError": "API hatası",
"error.chain.status": "Durum: {{status}}",
"error.chain.retryable": "Yeniden denenebilir: {{retryable}}",
"error.chain.responseBody": "Yanıt gövdesi:\n{{body}}",
"error.chain.didYouMean": "Bunu mu demek istediniz: {{suggestions}}",
"error.chain.modelNotFound": "Model bulunamadı: {{provider}}/{{model}}",
"error.chain.checkConfig": "Yapılandırma dosyanızı (opencode.json) sağlayıcı/model adlarını kontrol edin",
"error.chain.mcpFailed":
'MCP sunucusu "{{name}}" başarısız oldu. Not: OpenCode henüz MCP kimlik doğrulamasını desteklemiyor.',
"error.chain.providerAuthFailed": "Sağlayıcı kimlik doğrulaması başarısız ({{provider}}): {{message}}",
"error.chain.providerInitFailed":
'"{{provider}}" sağlayıcısı başlatılamadı. Kimlik bilgilerini ve yapılandırmayı kontrol edin.',
"error.chain.configJsonInvalid": "{{path}} adresindeki yapılandırma dosyası geçerli JSON(C) değil",
"error.chain.configJsonInvalidWithMessage":
"{{path}} adresindeki yapılandırma dosyası geçerli JSON(C) değil: {{message}}",
"error.chain.configDirectoryTypo":
'"{{dir}}" dizini {{path}} içinde geçerli değil. Dizini "{{suggestion}}" olarak yeniden adlandırın veya kaldırın. Bu yaygın bir yazım hatasıdır.',
"error.chain.configFrontmatterError": "{{path}} içindeki ön bilgi ayrıştırılamadı:\n{{message}}",
"error.chain.configInvalid": "{{path}} adresindeki yapılandırma dosyası geçersiz",
"error.chain.configInvalidWithMessage": "{{path}} adresindeki yapılandırma dosyası geçersiz: {{message}}",
"notification.permission.title": "İzin gerekli",
"notification.permission.description": "{{projectName}} içindeki {{sessionTitle}} izin gerektiriyor",
"notification.question.title": "Soru",
"notification.question.description": "{{projectName}} içindeki {{sessionTitle}} bir soru soruyor",
"notification.action.goToSession": "Oturuma git",
"notification.session.responseReady.title": "Yanıt hazır",
"notification.session.error.title": "Oturum hatası",
"notification.session.error.fallbackDescription": "Bir hata oluştu",
"home.recentProjects": "Son projeler",
"home.empty.title": "Son proje yok",
"home.empty.description": "Yerel bir proje açarak başlayın",
"session.tab.session": "Oturum",
"session.tab.review": "İnceleme",
"session.tab.context": "Bağlam",
"session.panel.reviewAndFiles": "İnceleme ve dosyalar",
"session.review.filesChanged": "{{count}} Dosya Değişti",
"session.review.change.one": "Değişiklik",
"session.review.change.other": "Değişiklik",
"session.review.loadingChanges": "Değişiklikler yükleniyor...",
"session.review.empty": "Bu oturumda henüz değişiklik yok",
"session.review.noVcs": "Git VCS algılanamadı, oturum değişiklikleri tespit edilemeyecek",
"session.review.noChanges": "Değişiklik yok",
"session.files.selectToOpen": "Açmak için bir dosya seçin",
"session.files.all": "Tüm dosyalar",
"session.files.binaryContent": "İkili dosya (içerik görüntülenemiyor)",
"session.messages.renderEarlier": "Önceki mesajları göster",
"session.messages.loadingEarlier": "Önceki mesajlar yükleniyor...",
"session.messages.loadEarlier": "Önceki mesajları yükle",
"session.messages.loading": "Mesajlar yükleniyor...",
"session.messages.jumpToLatest": "En sona atla",
"session.context.addToContext": "{{selection}} bağlama ekle",
"session.todo.title": "Görevler",
"session.todo.collapse": "Daralt",
"session.todo.expand": "Genişlet",
"session.new.worktree.main": "Ana dal",
"session.new.worktree.mainWithBranch": "Ana dal ({{branch}})",
"session.new.worktree.create": "Yeni çalışma ağacı oluştur",
"session.new.lastModified": "Son değişiklik",
"session.header.search.placeholder": "{{project}} ara",
"session.header.searchFiles": "Dosya ara",
"session.header.openIn": "Aç",
"session.header.open.action": "{{app}} ile aç",
"session.header.open.ariaLabel": "{{app}} ile aç",
"session.header.open.menu": "Açma seçenekleri",
"session.header.open.copyPath": "Yolu kopyala",
"status.popover.trigger": "Durum",
"status.popover.ariaLabel": "Sunucu yapılandırmaları",
"status.popover.tab.servers": "Sunucular",
"status.popover.tab.mcp": "MCP",
"status.popover.tab.lsp": "LSP",
"status.popover.tab.plugins": "Eklentiler",
"status.popover.action.manageServers": "Sunucuları yönet",
"session.share.popover.title": "Web'de yayınla",
"session.share.popover.description.shared": "Bu oturum web'de herkese açıktır. Bağlantıya sahip herkes erişebilir.",
"session.share.popover.description.unshared":
"Oturumu web'de herkese açık olarak paylaşın. Bağlantıya sahip herkes erişebilecek.",
"session.share.action.share": "Paylaş",
"session.share.action.publish": "Yayınla",
"session.share.action.publishing": "Yayınlanıyor...",
"session.share.action.unpublish": "Yayından kaldır",
"session.share.action.unpublishing": "Yayından kaldırılıyor...",
"session.share.action.view": "Görüntüle",
"session.share.copy.copied": "Kopyalandı",
"session.share.copy.copyLink": "Bağlantı kopyala",
"lsp.tooltip.none": "LSP sunucusu yok",
"lsp.label.connected": "{{count}} LSP",
"prompt.loading": "Komut yükleniyor...",
"terminal.loading": "Terminal yükleniyor...",
"terminal.title": "Terminal",
"terminal.title.numbered": "Terminal {{number}}",
"terminal.close": "Terminali kapat",
"terminal.connectionLost.title": "Bağlantı Kesildi",
"terminal.connectionLost.description":
"Terminal bağlantısı kesildi. Bu durum sunucu yeniden başladığında oluşabilir.",
"common.closeTab": "Sekmeyi kapat",
"common.dismiss": "Kapat",
"common.requestFailed": "İstek başarısız",
"common.moreOptions": "Daha fazla seçenek",
"common.learnMore": "Daha fazla bilgi",
"common.rename": "Yeniden adlandır",
"common.reset": "Sıfırla",
"common.archive": "Arşivle",
"common.delete": "Sil",
"common.close": "Kapat",
"common.edit": "Düzenle",
"common.loadMore": "Daha fazla yükle",
"common.key.esc": "ESC",
"sidebar.menu.toggle": "Menüyü aç/kapat",
"sidebar.nav.projectsAndSessions": "Projeler ve oturumlar",
"sidebar.settings": "Ayarlar",
"sidebar.help": "Yardım",
"sidebar.workspaces.enable": "Çalışma alanlarını etkinleştir",
"sidebar.workspaces.disable": "Çalışma alanlarını devre dışı bırak",
"sidebar.gettingStarted.title": "Başlarken",
"sidebar.gettingStarted.line1": "OpenCode ücretsiz modeller içerir, böylece hemen başlayabilirsiniz.",
"sidebar.gettingStarted.line2": "Claude, GPT, Gemini vb. modelleri kullanmak için herhangi bir sağlayıcı bağlayın.",
"sidebar.project.recentSessions": "Son oturumlar",
"sidebar.project.viewAllSessions": "Tüm oturumları görüntüle",
"sidebar.project.clearNotifications": "Bildirimleri temizle",
"app.name.desktop": "OpenCode Masaüstü",
"settings.section.desktop": "Masaüstü",
"settings.section.server": "Sunucu",
"settings.tab.general": "Genel",
"settings.tab.shortcuts": "Kısayollar",
"settings.desktop.section.wsl": "WSL",
"settings.desktop.wsl.title": "WSL entegrasyonu",
"settings.desktop.wsl.description": "OpenCode sunucusunu Windows'ta WSL içinde çalıştırın.",
"settings.general.section.appearance": "Görünüm",
"settings.general.section.notifications": "Sistem bildirimleri",
"settings.general.section.updates": "Güncellemeler",
"settings.general.section.sounds": "Ses efektleri",
"settings.general.section.feed": "Akış",
"settings.general.section.display": "Ekran",
"settings.general.row.language.title": "Dil",
"settings.general.row.language.description": "OpenCode'un görünüm dilini değiştirin",
"settings.general.row.appearance.title": "Görünüm",
"settings.general.row.appearance.description": "OpenCode'un cihazınızdaki görünümünü özelleştirin",
"settings.general.row.theme.title": "Tema",
"settings.general.row.theme.description": "OpenCode'un temasını özelleştirin.",
"settings.general.row.font.title": "Yazı Tipi",
"settings.general.row.font.description": "Kod bloklarında kullanılan monospace yazı tipini özelleştirin",
"settings.general.row.reasoningSummaries.title": "Akıl yürütme özetlerini göster",
"settings.general.row.reasoningSummaries.description": "Zaman çizelgesinde model akıl yürütme özetlerini görüntüle",
"settings.general.row.shellToolPartsExpanded.title": "Kabuk araç bileşenlerini genişlet",
"settings.general.row.shellToolPartsExpanded.description":
"Zaman çizelgesinde kabuk araç bileşenlerini varsayılan olarak genişletilmiş göster",
"settings.general.row.editToolPartsExpanded.title": "Düzenleme araç bileşenlerini genişlet",
"settings.general.row.editToolPartsExpanded.description":
"Zaman çizelgesinde düzenleme, yazma ve yama araç bileşenlerini varsayılan olarak genişletilmiş göster",
"settings.general.row.wayland.title": "Yerel Wayland kullan",
"settings.general.row.wayland.description":
"Wayland'da X11 geri dönüşünü devre dışı bırak. Yeniden başlatma gerektirir.",
"settings.general.row.wayland.tooltip":
"Karışık yenileme hızlı monitörlere sahip Linux'ta yerel Wayland daha kararlı olabilir.",
"settings.general.row.releaseNotes.title": "Sürüm notları",
"settings.general.row.releaseNotes.description": "Güncellemelerden sonra Yenilikler bildirimlerini göster",
"settings.updates.row.startup.title": "Başlangıçta güncellemeleri kontrol et",
"settings.updates.row.startup.description": "OpenCode başladığında otomatik güncelleme kontrolü yap",
"settings.updates.row.check.title": "Güncellemeleri kontrol et",
"settings.updates.row.check.description": "Elle güncelleme kontrolü yap ve varsa yükle",
"settings.updates.action.checkNow": "Şimdi kontrol et",
"settings.updates.action.checking": "Kontrol ediliyor...",
"settings.updates.toast.latest.title": "Güncelsiniz",
"settings.updates.toast.latest.description": "OpenCode'un en son sürümünü kullanıyorsunuz.",
"font.option.ibmPlexMono": "IBM Plex Mono",
"font.option.cascadiaCode": "Cascadia Code",
"font.option.firaCode": "Fira Code",
"font.option.hack": "Hack",
"font.option.inconsolata": "Inconsolata",
"font.option.intelOneMono": "Intel One Mono",
"font.option.iosevka": "Iosevka",
"font.option.jetbrainsMono": "JetBrains Mono",
"font.option.mesloLgs": "Meslo LGS",
"font.option.robotoMono": "Roboto Mono",
"font.option.sourceCodePro": "Source Code Pro",
"font.option.ubuntuMono": "Ubuntu Mono",
"font.option.geistMono": "Geist Mono",
"sound.option.none": "Yok",
"sound.option.alert01": "Uyarı 01",
"sound.option.alert02": "Uyarı 02",
"sound.option.alert03": "Uyarı 03",
"sound.option.alert04": "Uyarı 04",
"sound.option.alert05": "Uyarı 05",
"sound.option.alert06": "Uyarı 06",
"sound.option.alert07": "Uyarı 07",
"sound.option.alert08": "Uyarı 08",
"sound.option.alert09": "Uyarı 09",
"sound.option.alert10": "Uyarı 10",
"sound.option.bipbop01": "Bip-bop 01",
"sound.option.bipbop02": "Bip-bop 02",
"sound.option.bipbop03": "Bip-bop 03",
"sound.option.bipbop04": "Bip-bop 04",
"sound.option.bipbop05": "Bip-bop 05",
"sound.option.bipbop06": "Bip-bop 06",
"sound.option.bipbop07": "Bip-bop 07",
"sound.option.bipbop08": "Bip-bop 08",
"sound.option.bipbop09": "Bip-bop 09",
"sound.option.bipbop10": "Bip-bop 10",
"sound.option.staplebops01": "Staplebops 01",
"sound.option.staplebops02": "Staplebops 02",
"sound.option.staplebops03": "Staplebops 03",
"sound.option.staplebops04": "Staplebops 04",
"sound.option.staplebops05": "Staplebops 05",
"sound.option.staplebops06": "Staplebops 06",
"sound.option.staplebops07": "Staplebops 07",
"sound.option.nope01": "Hayır 01",
"sound.option.nope02": "Hayır 02",
"sound.option.nope03": "Hayır 03",
"sound.option.nope04": "Hayır 04",
"sound.option.nope05": "Hayır 05",
"sound.option.nope06": "Hayır 06",
"sound.option.nope07": "Hayır 07",
"sound.option.nope08": "Hayır 08",
"sound.option.nope09": "Hayır 09",
"sound.option.nope10": "Hayır 10",
"sound.option.nope11": "Hayır 11",
"sound.option.nope12": "Hayır 12",
"sound.option.yup01": "Evet 01",
"sound.option.yup02": "Evet 02",
"sound.option.yup03": "Evet 03",
"sound.option.yup04": "Evet 04",
"sound.option.yup05": "Evet 05",
"sound.option.yup06": "Evet 06",
"settings.general.notifications.agent.title": "Ajan",
"settings.general.notifications.agent.description":
"Ajan tamamlandığında veya dikkat gerektirdiğinde sistem bildirimi göster",
"settings.general.notifications.permissions.title": "İzinler",
"settings.general.notifications.permissions.description": "İzin gerektiğinde sistem bildirimi göster",
"settings.general.notifications.errors.title": "Hatalar",
"settings.general.notifications.errors.description": "Hata oluştuğunda sistem bildirimi göster",
"settings.general.sounds.agent.title": "Ajan",
"settings.general.sounds.agent.description": "Ajan tamamlandığında veya dikkat gerektirdiğinde ses çal",
"settings.general.sounds.permissions.title": "İzinler",
"settings.general.sounds.permissions.description": "İzin gerektiğinde ses çal",
"settings.general.sounds.errors.title": "Hatalar",
"settings.general.sounds.errors.description": "Hata oluştuğunda ses çal",
"settings.shortcuts.title": "Klavye kısayolları",
"settings.shortcuts.reset.button": "Varsayılanlara sıfırla",
"settings.shortcuts.reset.toast.title": "Kısayollar sıfırlandı",
"settings.shortcuts.reset.toast.description": "Klavye kısayolları varsayılanlara sıfırlandı.",
"settings.shortcuts.conflict.title": "Kısayol zaten kullanılıyor",
"settings.shortcuts.conflict.description": "{{keybind}} zaten {{titles}} için atanmış.",
"settings.shortcuts.unassigned": "Atanmamış",
"settings.shortcuts.pressKeys": "Tuşlara basın",
"settings.shortcuts.search.placeholder": "Kısayol ara",
"settings.shortcuts.search.empty": "Kısayol bulunamadı",
"settings.shortcuts.group.general": "Genel",
"settings.shortcuts.group.session": "Oturum",
"settings.shortcuts.group.navigation": "Gezinme",
"settings.shortcuts.group.modelAndAgent": "Model ve ajan",
"settings.shortcuts.group.terminal": "Terminal",
"settings.shortcuts.group.prompt": "Komut",
"settings.providers.title": "Sağlayıcılar",
"settings.providers.description": "Sağlayıcı ayarları burada yapılandırılabilecek.",
"settings.providers.section.connected": "Bağlı sağlayıcılar",
"settings.providers.connected.empty": "Bağlı sağlayıcı yok",
"settings.providers.section.popular": "Popüler sağlayıcılar",
"settings.providers.tag.environment": "Ortam",
"settings.providers.tag.config": "Yapılandırma",
"settings.providers.tag.custom": "Özel",
"settings.providers.tag.other": "Diğer",
"settings.models.title": "Modeller",
"settings.models.description": "Model ayarları burada yapılandırılabilecek.",
"settings.agents.title": "Ajanlar",
"settings.agents.description": "Ajan ayarları burada yapılandırılabilecek.",
"settings.commands.title": "Komutlar",
"settings.commands.description": "Komut ayarları burada yapılandırılabilecek.",
"settings.mcp.title": "MCP",
"settings.mcp.description": "MCP ayarları burada yapılandırılabilecek.",
"settings.permissions.title": "İzinler",
"settings.permissions.description": "Sunucunun varsayılan olarak hangi araçları kullanabileceğini kontrol edin.",
"settings.permissions.section.tools": "Araçlar",
"settings.permissions.toast.updateFailed.title": "İzinler güncellenemedi",
"settings.permissions.action.allow": "İzin Ver",
"settings.permissions.action.ask": "Sor",
"settings.permissions.action.deny": "Reddet",
"settings.permissions.tool.read.title": "Oku",
"settings.permissions.tool.read.description": "Bir dosyayı okuma (dosya yoluyla eşleşir)",
"settings.permissions.tool.edit.title": "Düzenle",
"settings.permissions.tool.edit.description": "Düzenleme, yazma, yama ve çoklu düzenleme dahil dosyaları değiştir",
"settings.permissions.tool.glob.title": "Glob",
"settings.permissions.tool.glob.description": "Glob kalıpları kullanarak dosyaları eşle",
"settings.permissions.tool.grep.title": "Grep",
"settings.permissions.tool.grep.description": "Düzenli ifadeler kullanarak dosya içerikleri ara",
"settings.permissions.tool.list.title": "Listele",
"settings.permissions.tool.list.description": "Bir dizindeki dosyaları listele",
"settings.permissions.tool.bash.title": "Bash",
"settings.permissions.tool.bash.description": "Kabuk komutları çalıştır",
"settings.permissions.tool.task.title": "Görev",
"settings.permissions.tool.task.description": "Alt ajanlar başlat",
"settings.permissions.tool.skill.title": "Beceri",
"settings.permissions.tool.skill.description": "Ada göre bir beceri yükle",
"settings.permissions.tool.lsp.title": "LSP",
"settings.permissions.tool.lsp.description": "Dil sunucusu sorguları çalıştır",
"settings.permissions.tool.todoread.title": "Görev Oku",
"settings.permissions.tool.todoread.description": "Görev listesini oku",
"settings.permissions.tool.todowrite.title": "Görev Yaz",
"settings.permissions.tool.todowrite.description": "Görev listesini güncelle",
"settings.permissions.tool.webfetch.title": "Web Getir",
"settings.permissions.tool.webfetch.description": "Bir URL'den içerik getir",
"settings.permissions.tool.websearch.title": "Web Ara",
"settings.permissions.tool.websearch.description": "Web'de ara",
"settings.permissions.tool.codesearch.title": "Kod Ara",
"settings.permissions.tool.codesearch.description": "Web'de kod ara",
"settings.permissions.tool.external_directory.title": "Harici Dizin",
"settings.permissions.tool.external_directory.description": "Proje dizini dışındaki dosyalara eriş",
"settings.permissions.tool.doom_loop.title": "Sonsuz Döngü",
"settings.permissions.tool.doom_loop.description": "Aynı girdiyle tekrarlanan araç çağrılarını algıla",
"session.delete.failed.title": "Oturum silinemedi",
"session.delete.title": "Oturumu sil",
"session.delete.confirm": '"{{name}}" oturumu silinsin mi?',
"session.delete.button": "Oturumu sil",
"workspace.new": "Yeni çalışma alanı",
"workspace.type.local": "yerel",
"workspace.type.sandbox": "sandbox",
"workspace.create.failed.title": "Çalışma alanı oluşturulamadı",
"workspace.delete.failed.title": "Çalışma alanı silinemedi",
"workspace.resetting.title": "Çalışma alanı sıfırlanıyor",
"workspace.resetting.description": "Bu bir dakika sürebilir.",
"workspace.reset.failed.title": "Çalışma alanı sıfırlanamadı",
"workspace.reset.success.title": "Çalışma alanı sıfırlandı",
"workspace.reset.success.description": "Çalışma alanı artık varsayılan dalla eşleşiyor.",
"workspace.error.stillPreparing": "Çalışma alanı hâlâ hazırlanıyor",
"workspace.status.checking": "Birleşmemiş değişiklikler kontrol ediliyor...",
"workspace.status.error": "Git durumu doğrulanamadı.",
"workspace.status.clean": "Birleşmemiş değişiklik algılanmadı.",
"workspace.status.dirty": "Bu çalışma alanında birleşmemiş değişiklikler algılandı.",
"workspace.delete.title": "Çalışma alanını sil",
"workspace.delete.confirm": '"{{name}}" çalışma alanı silinsin mi?',
"workspace.delete.button": "Çalışma alanını sil",
"workspace.reset.title": "Çalışma alanını sıfırla",
"workspace.reset.confirm": '"{{name}}" çalışma alanı sıfırlansın mı?',
"workspace.reset.button": "Çalışma alanını sıfırla",
"workspace.reset.archived.none": "Arşivlenecek aktif oturum yok.",
"workspace.reset.archived.one": "1 oturum arşivlenecek.",
"workspace.reset.archived.many": "{{count}} oturum arşivlenecek.",
"workspace.reset.note": "Bu işlem çalışma alanını varsayılan dalla eşleşecek şekilde sıfırlayacak.",
"common.open": "Aç",
"dialog.releaseNotes.action.getStarted": "Başla",
"dialog.releaseNotes.action.next": "İleri",
"dialog.releaseNotes.action.hideFuture": "Bunu gelecekte bir daha gösterme",
"dialog.releaseNotes.media.alt": "Sürüm önizlemesi",
"toast.project.reloadFailed.title": "{{project}} yeniden yüklenemedi",
"error.server.invalidConfiguration": "Geçersiz yapılandırma",
"common.moreCountSuffix": " (+{{count}} daha)",
"common.time.justNow": "Şimdi",
"common.time.minutesAgo.short": "{{count}}dk önce",
"common.time.hoursAgo.short": "{{count}}sa önce",
"common.time.daysAgo.short": "{{count}}g önce",
"settings.providers.connected.environmentDescription": "Ortam değişkenlerinizden bağlandı",
"settings.providers.custom.description": "Temel URL üzerinden OpenAI uyumlu bir sağlayıcı ekleyin.",
} satisfies Partial<Record<Keys, string>>

View File

@@ -809,4 +809,18 @@ export const dict = {
"workspace.reset.archived.one": "将归档 1 个会话。",
"workspace.reset.archived.many": "将归档 {{count}} 个会话。",
"workspace.reset.note": "这将把工作区重置为与默认分支一致。",
"common.open": "打开",
"dialog.releaseNotes.action.getStarted": "开始",
"dialog.releaseNotes.action.next": "下一步",
"dialog.releaseNotes.action.hideFuture": "不再显示",
"dialog.releaseNotes.media.alt": "发布预览",
"toast.project.reloadFailed.title": "无法重新加载 {{project}}",
"error.server.invalidConfiguration": "配置无效",
"common.moreCountSuffix": " (还有 {{count}} 个)",
"common.time.justNow": "刚刚",
"common.time.minutesAgo.short": "{{count}}分钟前",
"common.time.hoursAgo.short": "{{count}}小时前",
"common.time.daysAgo.short": "{{count}}天前",
"settings.providers.connected.environmentDescription": "已通过环境变量连接",
"settings.providers.custom.description": "通过基础 URL 添加与 OpenAI 兼容的提供商。",
} satisfies Partial<Record<Keys, string>>

View File

@@ -804,4 +804,18 @@ export const dict = {
"workspace.reset.archived.one": "將封存 1 個工作階段。",
"workspace.reset.archived.many": "將封存 {{count}} 個工作階段。",
"workspace.reset.note": "這將把工作區重設為與預設分支一致。",
"common.open": "打開",
"dialog.releaseNotes.action.getStarted": "開始",
"dialog.releaseNotes.action.next": "下一步",
"dialog.releaseNotes.action.hideFuture": "不再顯示",
"dialog.releaseNotes.media.alt": "發佈預覽",
"toast.project.reloadFailed.title": "無法重新載入 {{project}}",
"error.server.invalidConfiguration": "無效的設定",
"common.moreCountSuffix": " (還有 {{count}} 個)",
"common.time.justNow": "剛剛",
"common.time.minutesAgo.short": "{{count}}分鐘前",
"common.time.hoursAgo.short": "{{count}}小時前",
"common.time.daysAgo.short": "{{count}}天前",
"settings.providers.connected.environmentDescription": "已從環境變數連線",
"settings.providers.custom.description": "透過基本 URL 新增與 OpenAI 相容的提供者。",
} satisfies Partial<Record<Keys, string>>

View File

@@ -49,6 +49,7 @@ export function SessionReviewTab(props: SessionReviewTabProps) {
let scroll: HTMLDivElement | undefined
let restoreFrame: number | undefined
let userInteracted = false
let restored: { x: number; y: number } | undefined
const sdk = useSDK()
const layout = useLayout()
@@ -65,6 +66,11 @@ export function SessionReviewTab(props: SessionReviewTabProps) {
const handleInteraction = () => {
userInteracted = true
if (restoreFrame !== undefined) {
cancelAnimationFrame(restoreFrame)
restoreFrame = undefined
}
}
const doRestore = () => {
@@ -82,8 +88,11 @@ export function SessionReviewTab(props: SessionReviewTabProps) {
const targetY = Math.min(s.y, maxY)
const targetX = Math.min(s.x, maxX)
if (el.scrollTop === targetY && el.scrollLeft === targetX) return
if (el.scrollTop !== targetY) el.scrollTop = targetY
if (el.scrollLeft !== targetX) el.scrollLeft = targetX
restored = { x: el.scrollLeft, y: el.scrollTop }
}
const queueRestore = () => {
@@ -92,9 +101,16 @@ export function SessionReviewTab(props: SessionReviewTabProps) {
}
const handleScroll = (event: Event & { currentTarget: HTMLDivElement }) => {
if (!layout.ready() || !userInteracted) return
const el = event.currentTarget
const prev = restored
if (prev && el.scrollTop === prev.y && el.scrollLeft === prev.x) {
restored = undefined
return
}
restored = undefined
handleInteraction()
if (!layout.ready()) return
if (el.clientHeight === 0 || el.clientWidth === 0) return
props.view().setScroll("review", {
@@ -133,10 +149,11 @@ export function SessionReviewTab(props: SessionReviewTabProps) {
onCleanup(() => {
if (restoreFrame !== undefined) cancelAnimationFrame(restoreFrame)
if (scroll) {
scroll.removeEventListener("wheel", handleInteraction)
scroll.removeEventListener("pointerdown", handleInteraction)
scroll.removeEventListener("touchstart", handleInteraction)
scroll.removeEventListener("keydown", handleInteraction)
scroll.removeEventListener("wheel", handleInteraction, { capture: true })
scroll.removeEventListener("mousewheel", handleInteraction, { capture: true })
scroll.removeEventListener("pointerdown", handleInteraction, { capture: true })
scroll.removeEventListener("touchstart", handleInteraction, { capture: true })
scroll.removeEventListener("keydown", handleInteraction, { capture: true })
}
})
@@ -147,6 +164,7 @@ export function SessionReviewTab(props: SessionReviewTabProps) {
scrollRef={(el) => {
scroll = el
el.addEventListener("wheel", handleInteraction, { passive: true, capture: true })
el.addEventListener("mousewheel", handleInteraction, { passive: true, capture: true })
el.addEventListener("pointerdown", handleInteraction, { passive: true, capture: true })
el.addEventListener("touchstart", handleInteraction, { passive: true, capture: true })
el.addEventListener("keydown", handleInteraction, { passive: true, capture: true })

View File

@@ -7,11 +7,28 @@ export type ConfigInvalidError = {
}
}
export function formatServerError(error: unknown) {
if (isConfigInvalidErrorLike(error)) return parseReabaleConfigInvalidError(error)
type Label = {
unknown: string
invalidConfiguration: string
}
const fallback: Label = {
unknown: "Unknown error",
invalidConfiguration: "Invalid configuration",
}
function resolveLabel(labels: Partial<Label> | undefined): Label {
return {
unknown: labels?.unknown ?? fallback.unknown,
invalidConfiguration: labels?.invalidConfiguration ?? fallback.invalidConfiguration,
}
}
export function formatServerError(error: unknown, labels?: Partial<Label>) {
if (isConfigInvalidErrorLike(error)) return parseReabaleConfigInvalidError(error, labels)
if (error instanceof Error && error.message) return error.message
if (typeof error === "string" && error) return error
return "Unknown error"
return resolveLabel(labels).unknown
}
function isConfigInvalidErrorLike(error: unknown): error is ConfigInvalidError {
@@ -20,8 +37,8 @@ function isConfigInvalidErrorLike(error: unknown): error is ConfigInvalidError {
return o.name === "ConfigInvalidError" && typeof o.data === "object" && o.data !== null
}
export function parseReabaleConfigInvalidError(errorInput: ConfigInvalidError) {
const head = "Invalid configuration"
export function parseReabaleConfigInvalidError(errorInput: ConfigInvalidError, labels?: Partial<Label>) {
const head = resolveLabel(labels).invalidConfiguration
const file = errorInput.data.path && errorInput.data.path !== "config" ? errorInput.data.path : ""
const detail = errorInput.data.message?.trim() ?? ""
const issues = (errorInput.data.issues ?? []).map((issue) => {

View File

@@ -1,4 +1,12 @@
export function getRelativeTime(dateString: string): string {
type TimeKey =
| "common.time.justNow"
| "common.time.minutesAgo.short"
| "common.time.hoursAgo.short"
| "common.time.daysAgo.short"
type Translate = (key: TimeKey, params?: Record<string, string | number>) => string
export function getRelativeTime(dateString: string, t: Translate): string {
const date = new Date(dateString)
const now = new Date()
const diffMs = now.getTime() - date.getTime()
@@ -7,8 +15,8 @@ export function getRelativeTime(dateString: string): string {
const diffHours = Math.floor(diffMinutes / 60)
const diffDays = Math.floor(diffHours / 24)
if (diffSeconds < 60) return "Just now"
if (diffMinutes < 60) return `${diffMinutes}m ago`
if (diffHours < 24) return `${diffHours}h ago`
return `${diffDays}d ago`
if (diffSeconds < 60) return t("common.time.justNow")
if (diffMinutes < 60) return t("common.time.minutesAgo.short", { count: diffMinutes })
if (diffHours < 24) return t("common.time.hoursAgo.short", { count: diffHours })
return t("common.time.daysAgo.short", { count: diffDays })
}

View File

@@ -7,9 +7,21 @@ import { Font } from "@opencode-ai/ui/font"
import "@ibm/plex/css/ibm-plex.css"
import "./app.css"
import { LanguageProvider } from "~/context/language"
import { I18nProvider } from "~/context/i18n"
import { I18nProvider, useI18n } from "~/context/i18n"
import { strip } from "~/lib/language"
function AppMeta() {
const i18n = useI18n()
return (
<>
<Title>opencode</Title>
<Meta name="description" content={i18n.t("app.meta.description")} />
<Favicon />
<Font />
</>
)
}
export default function App() {
return (
<Router
@@ -19,10 +31,7 @@ export default function App() {
<LanguageProvider>
<I18nProvider>
<MetaProvider>
<Title>opencode</Title>
<Meta name="description" content="OpenCode - The open source coding agent." />
<Favicon />
<Font />
<AppMeta />
<Suspense>{props.children}</Suspense>
</MetaProvider>
</I18nProvider>

View File

@@ -124,8 +124,8 @@ export function Header(props: { zen?: boolean; hideGetStarted?: boolean }) {
<section data-component="top">
<div onContextMenu={handleLogoContextMenu}>
<A href={language.route("/")}>
<img data-slot="logo light" src={logoLight} alt="OpenCode" width="189" height="34" />
<img data-slot="logo dark" src={logoDark} alt="OpenCode" width="189" height="34" />
<img data-slot="logo light" src={logoLight} alt={i18n.t("nav.logoAlt")} width="189" height="34" />
<img data-slot="logo dark" src={logoDark} alt={i18n.t("nav.logoAlt")} width="189" height="34" />
</A>
</div>

View File

@@ -15,6 +15,7 @@ export const dict = {
"nav.home": "الرئيسية",
"nav.openMenu": "فتح القائمة",
"nav.getStartedFree": "ابدأ مجانا",
"nav.logoAlt": "OpenCode",
"nav.context.copyLogo": "نسخ الشعار كـ SVG",
"nav.context.copyWordmark": "نسخ اسم العلامة كـ SVG",
@@ -42,9 +43,13 @@ export const dict = {
"notFound.docs": "الوثائق",
"notFound.github": "GitHub",
"notFound.discord": "Discord",
"notFound.logoLightAlt": "شعار opencode الفاتح",
"notFound.logoDarkAlt": "شعار opencode الداكن",
"user.logout": "تسجيل الخروج",
"auth.callback.error.codeMissing": "لم يتم العثور على رمز التفويض.",
"workspace.select": "اختر مساحة العمل",
"workspace.createNew": "+ إنشاء مساحة عمل جديدة",
"workspace.modal.title": "إنشاء مساحة عمل جديدة",
@@ -76,6 +81,8 @@ export const dict = {
"error.reloadAmountMin": "يجب أن يكون مبلغ الشحن ${{amount}} على الأقل",
"error.reloadTriggerMin": "يجب أن يكون حد الرصيد ${{amount}} على الأقل",
"app.meta.description": "OpenCode - وكيل البرمجة مفتوح المصدر.",
"home.title": "OpenCode | وكيل برمجة بالذكاء الاصطناعي مفتوح المصدر",
"temp.title": "opencode | وكيل برمجة بالذكاء الاصطناعي مبني للطرفية",
@@ -91,6 +98,8 @@ export const dict = {
"temp.feature.models.afterLink": "، بما في ذلك النماذج المحلية",
"temp.screenshot.caption": "واجهة OpenCode الطرفية مع سمة tokyonight",
"temp.screenshot.alt": "واجهة OpenCode الطرفية بسمة tokyonight",
"temp.logoLightAlt": "شعار opencode الفاتح",
"temp.logoDarkAlt": "شعار opencode الداكن",
"home.banner.badge": "جديد",
"home.banner.text": "تطبيق سطح المكتب متاح بنسخة تجريبية",
@@ -238,6 +247,24 @@ export const dict = {
"تتم استضافة جميع نماذج Zen في الولايات المتحدة. يتبع المزودون سياسة عدم الاحتفاظ بالبيانات ولا يستخدمون بياناتك لتدريب النماذج، مع",
"zen.privacy.exceptionsLink": "الاستثناءات التالية",
"zen.api.error.rateLimitExceeded": "تم تجاوز حد الطلبات. يرجى المحاولة مرة أخرى لاحقًا.",
"zen.api.error.modelNotSupported": "النموذج {{model}} غير مدعوم",
"zen.api.error.modelFormatNotSupported": "النموذج {{model}} غير مدعوم للتنسيق {{format}}",
"zen.api.error.noProviderAvailable": "لا يوجد مزود متاح",
"zen.api.error.providerNotSupported": "المزود {{provider}} غير مدعوم",
"zen.api.error.missingApiKey": "مفتاح API مفقود.",
"zen.api.error.invalidApiKey": "مفتاح API غير صالح.",
"zen.api.error.subscriptionQuotaExceeded": "تم تجاوز حصة الاشتراك. أعد المحاولة خلال {{retryIn}}.",
"zen.api.error.subscriptionQuotaExceededUseFreeModels":
"تم تجاوز حصة الاشتراك. يمكنك الاستمرار في استخدام النماذج المجانية.",
"zen.api.error.noPaymentMethod": "لا توجد طريقة دفع. أضف طريقة دفع هنا: {{billingUrl}}",
"zen.api.error.insufficientBalance": "رصيد غير كاف. إدارة فواتيرك هنا: {{billingUrl}}",
"zen.api.error.workspaceMonthlyLimitReached":
"وصلت مساحة العمل الخاصة بك إلى حد الإنفاق الشهري البالغ ${{amount}}. إدارة حدودك هنا: {{billingUrl}}",
"zen.api.error.userMonthlyLimitReached":
"لقد وصلت إلى حد الإنفاق الشهري البالغ ${{amount}}. إدارة حدودك هنا: {{membersUrl}}",
"zen.api.error.modelDisabled": "النموذج معطل",
"black.meta.title": "OpenCode Black | الوصول إلى أفضل نماذج البرمجة في العالم",
"black.meta.description": "احصل على وصول إلى Claude، GPT، Gemini والمزيد مع خطط اشتراك OpenCode Black.",
"black.hero.title": "الوصول إلى أفضل نماذج البرمجة في العالم",
@@ -446,6 +473,7 @@ export const dict = {
"workspace.reload.updatePaymentMethod": "يرجى تحديث طريقة الدفع والمحاولة مرة أخرى.",
"workspace.reload.retrying": "جارٍ إعادة المحاولة...",
"workspace.reload.retry": "أعد المحاولة",
"workspace.reload.error.paymentFailed": "فشلت عملية الدفع.",
"workspace.payments.title": "سجل المدفوعات",
"workspace.payments.subtitle": "معاملات الدفع الأخيرة.",
@@ -563,6 +591,10 @@ export const dict = {
"enterprise.form.send": "إرسال",
"enterprise.form.sending": "جارٍ الإرسال...",
"enterprise.form.success": "تم إرسال الرسالة، سنتواصل معك قريبًا.",
"enterprise.form.success.submitted": "تم إرسال النموذج بنجاح.",
"enterprise.form.error.allFieldsRequired": "جميع الحقول مطلوبة.",
"enterprise.form.error.invalidEmailFormat": "تنسيق البريد الإلكتروني غير صالح.",
"enterprise.form.error.internalServer": "خطأ داخلي في الخادم.",
"enterprise.faq.title": "الأسئلة الشائعة",
"enterprise.faq.q1": "ما هو OpenCode Enterprise؟",
"enterprise.faq.a1":
@@ -595,6 +627,7 @@ export const dict = {
"bench.list.table.agent": "الوكيل",
"bench.list.table.model": "النموذج",
"bench.list.table.score": "الدرجة",
"bench.submission.error.allFieldsRequired": "جميع الحقول مطلوبة.",
"bench.detail.title": "المعيار - {{task}}",
"bench.detail.notFound": "المهمة غير موجودة",

View File

@@ -15,6 +15,7 @@ export const dict = {
"nav.home": "Início",
"nav.openMenu": "Abrir menu",
"nav.getStartedFree": "Começar grátis",
"nav.logoAlt": "OpenCode",
"nav.context.copyLogo": "Copiar logo como SVG",
"nav.context.copyWordmark": "Copiar marca como SVG",
@@ -42,9 +43,13 @@ export const dict = {
"notFound.docs": "Documentação",
"notFound.github": "GitHub",
"notFound.discord": "Discord",
"notFound.logoLightAlt": "logo opencode claro",
"notFound.logoDarkAlt": "logo opencode escuro",
"user.logout": "Sair",
"auth.callback.error.codeMissing": "Nenhum código de autorização encontrado.",
"workspace.select": "Selecionar workspace",
"workspace.createNew": "+ Criar novo workspace",
"workspace.modal.title": "Criar novo workspace",
@@ -76,6 +81,8 @@ export const dict = {
"error.reloadAmountMin": "O valor de recarga deve ser de pelo menos ${{amount}}",
"error.reloadTriggerMin": "O gatilho de saldo deve ser de pelo menos ${{amount}}",
"app.meta.description": "OpenCode - O agente de codificação de código aberto.",
"home.title": "OpenCode | O agente de codificação de código aberto com IA",
"temp.title": "opencode | Agente de codificação com IA feito para o terminal",
@@ -91,6 +98,8 @@ export const dict = {
"temp.feature.models.afterLink": ", incluindo modelos locais",
"temp.screenshot.caption": "OpenCode TUI com o tema tokyonight",
"temp.screenshot.alt": "OpenCode TUI com tema tokyonight",
"temp.logoLightAlt": "logo opencode claro",
"temp.logoDarkAlt": "logo opencode escuro",
"home.banner.badge": "Novo",
"home.banner.text": "App desktop disponível em beta",
@@ -242,6 +251,24 @@ export const dict = {
"Todos os modelos Zen são hospedados nos EUA. Os provedores seguem uma política de retenção zero e não usam seus dados para treinamento de modelo, com as",
"zen.privacy.exceptionsLink": "seguintes exceções",
"zen.api.error.rateLimitExceeded": "Limite de taxa excedido. Por favor, tente novamente mais tarde.",
"zen.api.error.modelNotSupported": "Modelo {{model}} não suportado",
"zen.api.error.modelFormatNotSupported": "Modelo {{model}} não suportado para o formato {{format}}",
"zen.api.error.noProviderAvailable": "Nenhum provedor disponível",
"zen.api.error.providerNotSupported": "Provedor {{provider}} não suportado",
"zen.api.error.missingApiKey": "Chave de API ausente.",
"zen.api.error.invalidApiKey": "Chave de API inválida.",
"zen.api.error.subscriptionQuotaExceeded": "Cota de assinatura excedida. Tente novamente em {{retryIn}}.",
"zen.api.error.subscriptionQuotaExceededUseFreeModels":
"Cota de assinatura excedida. Você pode continuar usando modelos gratuitos.",
"zen.api.error.noPaymentMethod": "Nenhuma forma de pagamento. Adicione uma forma de pagamento aqui: {{billingUrl}}",
"zen.api.error.insufficientBalance": "Saldo insuficiente. Gerencie seu faturamento aqui: {{billingUrl}}",
"zen.api.error.workspaceMonthlyLimitReached":
"Seu workspace atingiu o limite de gastos mensais de ${{amount}}. Gerencie seus limites aqui: {{billingUrl}}",
"zen.api.error.userMonthlyLimitReached":
"Você atingiu seu limite de gastos mensais de ${{amount}}. Gerencie seus limites aqui: {{membersUrl}}",
"zen.api.error.modelDisabled": "O modelo está desabilitado",
"black.meta.title": "OpenCode Black | Acesse os melhores modelos de codificação do mundo",
"black.meta.description": "Tenha acesso ao Claude, GPT, Gemini e mais com os planos de assinatura OpenCode Black.",
"black.hero.title": "Acesse os melhores modelos de codificação do mundo",
@@ -451,6 +478,7 @@ export const dict = {
"workspace.reload.updatePaymentMethod": "Por favor, atualize sua forma de pagamento e tente novamente.",
"workspace.reload.retrying": "Tentando novamente...",
"workspace.reload.retry": "Tentar novamente",
"workspace.reload.error.paymentFailed": "Pagamento falhou.",
"workspace.payments.title": "Histórico de Pagamentos",
"workspace.payments.subtitle": "Transações de pagamento recentes.",
@@ -571,6 +599,10 @@ export const dict = {
"enterprise.form.send": "Enviar",
"enterprise.form.sending": "Enviando...",
"enterprise.form.success": "Mensagem enviada, entraremos em contato em breve.",
"enterprise.form.success.submitted": "Formulário enviado com sucesso.",
"enterprise.form.error.allFieldsRequired": "Todos os campos são obrigatórios.",
"enterprise.form.error.invalidEmailFormat": "Formato de e-mail inválido.",
"enterprise.form.error.internalServer": "Erro interno do servidor.",
"enterprise.faq.title": "FAQ",
"enterprise.faq.q1": "O que é OpenCode Enterprise?",
"enterprise.faq.a1":
@@ -603,6 +635,7 @@ export const dict = {
"bench.list.table.agent": "Agente",
"bench.list.table.model": "Modelo",
"bench.list.table.score": "Pontuação",
"bench.submission.error.allFieldsRequired": "Todos os campos são obrigatórios.",
"bench.detail.title": "Benchmark - {{task}}",
"bench.detail.notFound": "Tarefa não encontrada",

View File

@@ -15,6 +15,7 @@ export const dict = {
"nav.home": "Hjem",
"nav.openMenu": "Åbn menu",
"nav.getStartedFree": "Kom i gang gratis",
"nav.logoAlt": "OpenCode",
"nav.context.copyLogo": "Kopier logo som SVG",
"nav.context.copyWordmark": "Kopier wordmark som SVG",
@@ -42,9 +43,13 @@ export const dict = {
"notFound.docs": "Dokumentation",
"notFound.github": "GitHub",
"notFound.discord": "Discord",
"notFound.logoLightAlt": "opencode logo light",
"notFound.logoDarkAlt": "opencode logo dark",
"user.logout": "Log ud",
"auth.callback.error.codeMissing": "Ingen autorisationskode fundet.",
"workspace.select": "Vælg workspace",
"workspace.createNew": "+ Opret nyt workspace",
"workspace.modal.title": "Opret nyt workspace",
@@ -76,6 +81,8 @@ export const dict = {
"error.reloadAmountMin": "Genopfyldningsbeløb skal være mindst ${{amount}}",
"error.reloadTriggerMin": "Saldogrænse skal være mindst ${{amount}}",
"app.meta.description": "OpenCode - Den open source kodningsagent.",
"home.title": "OpenCode | Den open source AI-kodningsagent",
"temp.title": "opencode | AI-kodningsagent bygget til terminalen",
@@ -91,6 +98,8 @@ export const dict = {
"temp.feature.models.afterLink": ", inklusive lokale modeller",
"temp.screenshot.caption": "opencode TUI med tokyonight-temaet",
"temp.screenshot.alt": "opencode TUI med tokyonight-temaet",
"temp.logoLightAlt": "opencode logo light",
"temp.logoDarkAlt": "opencode logo dark",
"home.banner.badge": "Ny",
"home.banner.text": "Desktop-app tilgængelig i beta",
@@ -240,6 +249,24 @@ export const dict = {
"Alle Zen-modeller er hostet i USA. Udbydere følger en nulopbevaringspolitik og bruger ikke dine data til modeltræning med",
"zen.privacy.exceptionsLink": "følgende undtagelser",
"zen.api.error.rateLimitExceeded": "Hastighedsgrænse overskredet. Prøv venligst igen senere.",
"zen.api.error.modelNotSupported": "Model {{model}} understøttes ikke",
"zen.api.error.modelFormatNotSupported": "Model {{model}} understøttes ikke for format {{format}}",
"zen.api.error.noProviderAvailable": "Ingen udbyder tilgængelig",
"zen.api.error.providerNotSupported": "Udbyder {{provider}} understøttes ikke",
"zen.api.error.missingApiKey": "Manglende API-nøgle.",
"zen.api.error.invalidApiKey": "Ugyldig API-nøgle.",
"zen.api.error.subscriptionQuotaExceeded": "Abonnementskvote overskredet. Prøv igen om {{retryIn}}.",
"zen.api.error.subscriptionQuotaExceededUseFreeModels":
"Abonnementskvote overskredet. Du kan fortsætte med at bruge gratis modeller.",
"zen.api.error.noPaymentMethod": "Ingen betalingsmetode. Tilføj en betalingsmetode her: {{billingUrl}}",
"zen.api.error.insufficientBalance": "Utilstrækkelig saldo. Administrer din fakturering her: {{billingUrl}}",
"zen.api.error.workspaceMonthlyLimitReached":
"Dit workspace har nået sin månedlige forbrugsgrænse på ${{amount}}. Administrer dine grænser her: {{billingUrl}}",
"zen.api.error.userMonthlyLimitReached":
"Du har nået din månedlige forbrugsgrænse på ${{amount}}. Administrer dine grænser her: {{membersUrl}}",
"zen.api.error.modelDisabled": "Modellen er deaktiveret",
"black.meta.title": "OpenCode Black | Få adgang til verdens bedste kodningsmodeller",
"black.meta.description": "Få adgang til Claude, GPT, Gemini og mere med OpenCode Black-abonnementer.",
"black.hero.title": "Få adgang til verdens bedste kodningsmodeller",
@@ -449,6 +476,7 @@ export const dict = {
"workspace.reload.updatePaymentMethod": "Opdater din betalingsmetode, og prøv igen.",
"workspace.reload.retrying": "Prøver igen...",
"workspace.reload.retry": "Prøv igen",
"workspace.reload.error.paymentFailed": "Betaling mislykkedes.",
"workspace.payments.title": "Betalingshistorik",
"workspace.payments.subtitle": "Seneste betalingstransaktioner.",
@@ -567,6 +595,10 @@ export const dict = {
"enterprise.form.send": "Send",
"enterprise.form.sending": "Sender...",
"enterprise.form.success": "Besked sendt, vi vender tilbage snart.",
"enterprise.form.success.submitted": "Formular indsendt med succes.",
"enterprise.form.error.allFieldsRequired": "Alle felter er påkrævet.",
"enterprise.form.error.invalidEmailFormat": "Ugyldigt e-mailformat.",
"enterprise.form.error.internalServer": "Intern serverfejl.",
"enterprise.faq.title": "FAQ",
"enterprise.faq.q1": "Hvad er OpenCode Enterprise?",
"enterprise.faq.a1":
@@ -599,6 +631,7 @@ export const dict = {
"bench.list.table.agent": "Agent",
"bench.list.table.model": "Model",
"bench.list.table.score": "Score",
"bench.submission.error.allFieldsRequired": "Alle felter er påkrævet.",
"bench.detail.title": "Benchmark - {{task}}",
"bench.detail.notFound": "Opgave ikke fundet",

View File

@@ -15,6 +15,7 @@ export const dict = {
"nav.home": "Startseite",
"nav.openMenu": "Menü öffnen",
"nav.getStartedFree": "Kostenlos starten",
"nav.logoAlt": "OpenCode",
"nav.context.copyLogo": "Logo als SVG kopieren",
"nav.context.copyWordmark": "Wortmarke als SVG kopieren",
@@ -42,9 +43,13 @@ export const dict = {
"notFound.docs": "Dokumentation",
"notFound.github": "GitHub",
"notFound.discord": "Discord",
"notFound.logoLightAlt": "OpenCode Logo hell",
"notFound.logoDarkAlt": "OpenCode Logo dunkel",
"user.logout": "Abmelden",
"auth.callback.error.codeMissing": "Kein Autorisierungscode gefunden.",
"workspace.select": "Workspace auswählen",
"workspace.createNew": "+ Neuen Workspace erstellen",
"workspace.modal.title": "Neuen Workspace erstellen",
@@ -76,6 +81,8 @@ export const dict = {
"error.reloadAmountMin": "Aufladebetrag muss mindestens ${{amount}} betragen",
"error.reloadTriggerMin": "Guthaben-Auslöser muss mindestens ${{amount}} betragen",
"app.meta.description": "OpenCode - Der Open-Source Coding-Agent.",
"home.title": "OpenCode | Der Open-Source AI-Coding-Agent",
"temp.title": "OpenCode | Für das Terminal gebauter AI-Coding-Agent",
@@ -91,6 +98,8 @@ export const dict = {
"temp.feature.models.afterLink": ", einschließlich lokaler Modelle",
"temp.screenshot.caption": "OpenCode TUI mit dem Tokyonight-Theme",
"temp.screenshot.alt": "OpenCode TUI mit Tokyonight-Theme",
"temp.logoLightAlt": "OpenCode Logo hell",
"temp.logoDarkAlt": "OpenCode Logo dunkel",
"home.banner.badge": "Neu",
"home.banner.text": "Desktop-App in der Beta verfügbar",
@@ -242,6 +251,24 @@ export const dict = {
"Alle Zen-Modelle werden in den USA gehostet. Anbieter folgen einer Zero-Retention-Policy und nutzen deine Daten nicht für Modelltraining, mit den",
"zen.privacy.exceptionsLink": "folgenden Ausnahmen",
"zen.api.error.rateLimitExceeded": "Ratenlimit überschritten. Bitte versuche es später erneut.",
"zen.api.error.modelNotSupported": "Modell {{model}} wird nicht unterstützt",
"zen.api.error.modelFormatNotSupported": "Modell {{model}} wird für das Format {{format}} nicht unterstützt",
"zen.api.error.noProviderAvailable": "Kein Anbieter verfügbar",
"zen.api.error.providerNotSupported": "Anbieter {{provider}} wird nicht unterstützt",
"zen.api.error.missingApiKey": "Fehlender API-Key.",
"zen.api.error.invalidApiKey": "Ungültiger API-Key.",
"zen.api.error.subscriptionQuotaExceeded": "Abonnement-Quote überschritten. Erneuter Versuch in {{retryIn}}.",
"zen.api.error.subscriptionQuotaExceededUseFreeModels":
"Abonnement-Quote überschritten. Du kannst weiterhin kostenlose Modelle nutzen.",
"zen.api.error.noPaymentMethod": "Keine Zahlungsmethode. Füge hier eine Zahlungsmethode hinzu: {{billingUrl}}",
"zen.api.error.insufficientBalance": "Unzureichendes Guthaben. Verwalte deine Abrechnung hier: {{billingUrl}}",
"zen.api.error.workspaceMonthlyLimitReached":
"Dein Workspace hat sein monatliches Ausgabenlimit von ${{amount}} erreicht. Verwalte deine Limits hier: {{billingUrl}}",
"zen.api.error.userMonthlyLimitReached":
"Du hast dein monatliches Ausgabenlimit von ${{amount}} erreicht. Verwalte deine Limits hier: {{membersUrl}}",
"zen.api.error.modelDisabled": "Modell ist deaktiviert",
"black.meta.title": "OpenCode Black | Zugriff auf die weltweit besten Coding-Modelle",
"black.meta.description": "Erhalte Zugriff auf Claude, GPT, Gemini und mehr mit OpenCode Black Abos.",
"black.hero.title": "Zugriff auf die weltweit besten Coding-Modelle",
@@ -451,6 +478,7 @@ export const dict = {
"workspace.reload.updatePaymentMethod": "Bitte aktualisiere deine Zahlungsmethode und versuche es erneut.",
"workspace.reload.retrying": "Versuche erneut...",
"workspace.reload.retry": "Erneut versuchen",
"workspace.reload.error.paymentFailed": "Zahlung fehlgeschlagen.",
"workspace.payments.title": "Zahlungshistorie",
"workspace.payments.subtitle": "Kürzliche Zahlungstransaktionen.",
@@ -571,6 +599,10 @@ export const dict = {
"enterprise.form.send": "Senden",
"enterprise.form.sending": "Sende...",
"enterprise.form.success": "Nachricht gesendet, wir melden uns bald.",
"enterprise.form.success.submitted": "Formular erfolgreich gesendet.",
"enterprise.form.error.allFieldsRequired": "Alle Felder sind erforderlich.",
"enterprise.form.error.invalidEmailFormat": "Ungültiges E-Mail-Format.",
"enterprise.form.error.internalServer": "Interner Serverfehler.",
"enterprise.faq.title": "FAQ",
"enterprise.faq.q1": "Was ist OpenCode Enterprise?",
"enterprise.faq.a1":
@@ -603,6 +635,7 @@ export const dict = {
"bench.list.table.agent": "Agent",
"bench.list.table.model": "Modell",
"bench.list.table.score": "Score",
"bench.submission.error.allFieldsRequired": "Alle Felder sind erforderlich.",
"bench.detail.title": "Benchmark - {{task}}",
"bench.detail.notFound": "Task nicht gefunden",

View File

@@ -11,6 +11,7 @@ export const dict = {
"nav.home": "Home",
"nav.openMenu": "Open menu",
"nav.getStartedFree": "Get started for free",
"nav.logoAlt": "OpenCode",
"nav.context.copyLogo": "Copy logo as SVG",
"nav.context.copyWordmark": "Copy wordmark as SVG",
@@ -38,9 +39,13 @@ export const dict = {
"notFound.docs": "Docs",
"notFound.github": "GitHub",
"notFound.discord": "Discord",
"notFound.logoLightAlt": "opencode logo light",
"notFound.logoDarkAlt": "opencode logo dark",
"user.logout": "Logout",
"auth.callback.error.codeMissing": "No authorization code found.",
"workspace.select": "Select workspace",
"workspace.createNew": "+ Create New Workspace",
"workspace.modal.title": "Create New Workspace",
@@ -72,6 +77,8 @@ export const dict = {
"error.reloadAmountMin": "Reload amount must be at least ${{amount}}",
"error.reloadTriggerMin": "Balance trigger must be at least ${{amount}}",
"app.meta.description": "OpenCode - The open source coding agent.",
"home.title": "OpenCode | The open source AI coding agent",
"temp.title": "opencode | AI coding agent built for the terminal",
@@ -87,6 +94,8 @@ export const dict = {
"temp.feature.models.afterLink": ", including local models",
"temp.screenshot.caption": "opencode TUI with the tokyonight theme",
"temp.screenshot.alt": "opencode TUI with tokyonight theme",
"temp.logoLightAlt": "opencode logo light",
"temp.logoDarkAlt": "opencode logo dark",
"home.banner.badge": "New",
"home.banner.text": "Desktop app available in beta",
@@ -234,6 +243,24 @@ export const dict = {
"All Zen models are hosted in the US. Providers follow a zero-retention policy and do not use your data for model training, with the",
"zen.privacy.exceptionsLink": "following exceptions",
"zen.api.error.rateLimitExceeded": "Rate limit exceeded. Please try again later.",
"zen.api.error.modelNotSupported": "Model {{model}} not supported",
"zen.api.error.modelFormatNotSupported": "Model {{model}} not supported for format {{format}}",
"zen.api.error.noProviderAvailable": "No provider available",
"zen.api.error.providerNotSupported": "Provider {{provider}} not supported",
"zen.api.error.missingApiKey": "Missing API key.",
"zen.api.error.invalidApiKey": "Invalid API key.",
"zen.api.error.subscriptionQuotaExceeded": "Subscription quota exceeded. Retry in {{retryIn}}.",
"zen.api.error.subscriptionQuotaExceededUseFreeModels":
"Subscription quota exceeded. You can continue using free models.",
"zen.api.error.noPaymentMethod": "No payment method. Add a payment method here: {{billingUrl}}",
"zen.api.error.insufficientBalance": "Insufficient balance. Manage your billing here: {{billingUrl}}",
"zen.api.error.workspaceMonthlyLimitReached":
"Your workspace has reached its monthly spending limit of ${{amount}}. Manage your limits here: {{billingUrl}}",
"zen.api.error.userMonthlyLimitReached":
"You have reached your monthly spending limit of ${{amount}}. Manage your limits here: {{membersUrl}}",
"zen.api.error.modelDisabled": "Model is disabled",
"black.meta.title": "OpenCode Black | Access all the world's best coding models",
"black.meta.description": "Get access to Claude, GPT, Gemini and more with OpenCode Black subscription plans.",
"black.hero.title": "Access all the world's best coding models",
@@ -443,6 +470,7 @@ export const dict = {
"workspace.reload.updatePaymentMethod": "Please update your payment method and try again.",
"workspace.reload.retrying": "Retrying...",
"workspace.reload.retry": "Retry",
"workspace.reload.error.paymentFailed": "Payment failed.",
"workspace.payments.title": "Payments History",
"workspace.payments.subtitle": "Recent payment transactions.",
@@ -561,6 +589,10 @@ export const dict = {
"enterprise.form.send": "Send",
"enterprise.form.sending": "Sending...",
"enterprise.form.success": "Message sent, we'll be in touch soon.",
"enterprise.form.success.submitted": "Form submitted successfully.",
"enterprise.form.error.allFieldsRequired": "All fields are required.",
"enterprise.form.error.invalidEmailFormat": "Invalid email format.",
"enterprise.form.error.internalServer": "Internal server error.",
"enterprise.faq.title": "FAQ",
"enterprise.faq.q1": "What is OpenCode Enterprise?",
"enterprise.faq.a1":
@@ -593,6 +625,7 @@ export const dict = {
"bench.list.table.agent": "Agent",
"bench.list.table.model": "Model",
"bench.list.table.score": "Score",
"bench.submission.error.allFieldsRequired": "All fields are required.",
"bench.detail.title": "Benchmark - {{task}}",
"bench.detail.notFound": "Task not found",

View File

@@ -15,6 +15,7 @@ export const dict = {
"nav.home": "Inicio",
"nav.openMenu": "Abrir menú",
"nav.getStartedFree": "Empezar gratis",
"nav.logoAlt": "OpenCode",
"nav.context.copyLogo": "Copiar logo como SVG",
"nav.context.copyWordmark": "Copiar marca como SVG",
@@ -42,9 +43,13 @@ export const dict = {
"notFound.docs": "Documentación",
"notFound.github": "GitHub",
"notFound.discord": "Discord",
"notFound.logoLightAlt": "opencode logo claro",
"notFound.logoDarkAlt": "opencode logo oscuro",
"user.logout": "Cerrar sesión",
"auth.callback.error.codeMissing": "No se encontró código de autorización.",
"workspace.select": "Seleccionar espacio de trabajo",
"workspace.createNew": "+ Crear nuevo espacio de trabajo",
"workspace.modal.title": "Crear nuevo espacio de trabajo",
@@ -76,6 +81,8 @@ export const dict = {
"error.reloadAmountMin": "La cantidad de recarga debe ser al menos ${{amount}}",
"error.reloadTriggerMin": "El disparador de saldo debe ser al menos ${{amount}}",
"app.meta.description": "OpenCode - El agente de codificación de código abierto.",
"home.title": "OpenCode | El agente de codificación IA de código abierto",
"temp.title": "opencode | Agente de codificación IA creado para la terminal",
@@ -91,6 +98,8 @@ export const dict = {
"temp.feature.models.afterLink": ", incluyendo modelos locales",
"temp.screenshot.caption": "opencode TUI con el tema tokyonight",
"temp.screenshot.alt": "opencode TUI con tema tokyonight",
"temp.logoLightAlt": "logo de opencode claro",
"temp.logoDarkAlt": "logo de opencode oscuro",
"home.banner.badge": "Nuevo",
"home.banner.text": "Aplicación de escritorio disponible en beta",
@@ -243,6 +252,24 @@ export const dict = {
"Todos los modelos Zen están alojados en EE. UU. Los proveedores siguen una política de cero retención y no usan tus datos para entrenamiento de modelos, con las",
"zen.privacy.exceptionsLink": "siguientes excepciones",
"zen.api.error.rateLimitExceeded": "Límite de tasa excedido. Por favor, inténtalo de nuevo más tarde.",
"zen.api.error.modelNotSupported": "Modelo {{model}} no soportado",
"zen.api.error.modelFormatNotSupported": "Modelo {{model}} no soportado para el formato {{format}}",
"zen.api.error.noProviderAvailable": "Ningún proveedor disponible",
"zen.api.error.providerNotSupported": "Proveedor {{provider}} no soportado",
"zen.api.error.missingApiKey": "Falta la clave API.",
"zen.api.error.invalidApiKey": "Clave API inválida.",
"zen.api.error.subscriptionQuotaExceeded": "Cuota de suscripción excedida. Reintenta en {{retryIn}}.",
"zen.api.error.subscriptionQuotaExceededUseFreeModels":
"Cuota de suscripción excedida. Puedes continuar usando modelos gratuitos.",
"zen.api.error.noPaymentMethod": "Sin método de pago. Añade un método de pago aquí: {{billingUrl}}",
"zen.api.error.insufficientBalance": "Saldo insuficiente. Gestiona tu facturación aquí: {{billingUrl}}",
"zen.api.error.workspaceMonthlyLimitReached":
"Tu espacio de trabajo ha alcanzado su límite de gasto mensual de ${{amount}}. Gestiona tus límites aquí: {{billingUrl}}",
"zen.api.error.userMonthlyLimitReached":
"Has alcanzado tu límite de gasto mensual de ${{amount}}. Gestiona tus límites aquí: {{membersUrl}}",
"zen.api.error.modelDisabled": "El modelo está deshabilitado",
"black.meta.title": "OpenCode Black | Accede a los mejores modelos de codificación del mundo",
"black.meta.description": "Obtén acceso a Claude, GPT, Gemini y más con los planes de suscripción de OpenCode Black.",
"black.hero.title": "Accede a los mejores modelos de codificación del mundo",
@@ -452,6 +479,7 @@ export const dict = {
"workspace.reload.updatePaymentMethod": "Por favor actualiza tu método de pago e intenta de nuevo.",
"workspace.reload.retrying": "Reintentando...",
"workspace.reload.retry": "Reintentar",
"workspace.reload.error.paymentFailed": "El pago falló.",
"workspace.payments.title": "Historial de Pagos",
"workspace.payments.subtitle": "Transacciones de pago recientes.",
@@ -571,6 +599,10 @@ export const dict = {
"enterprise.form.send": "Enviar",
"enterprise.form.sending": "Enviando...",
"enterprise.form.success": "Mensaje enviado, estaremos en contacto pronto.",
"enterprise.form.success.submitted": "Formulario enviado con éxito.",
"enterprise.form.error.allFieldsRequired": "Todos los campos son obligatorios.",
"enterprise.form.error.invalidEmailFormat": "Formato de correo inválido.",
"enterprise.form.error.internalServer": "Error interno del servidor.",
"enterprise.faq.title": "FAQ",
"enterprise.faq.q1": "¿Qué es OpenCode Enterprise?",
"enterprise.faq.a1":
@@ -603,6 +635,7 @@ export const dict = {
"bench.list.table.agent": "Agente",
"bench.list.table.model": "Modelo",
"bench.list.table.score": "Puntuación",
"bench.submission.error.allFieldsRequired": "Todos los campos son obligatorios.",
"bench.detail.title": "Benchmark - {{task}}",
"bench.detail.notFound": "Tarea no encontrada",

View File

@@ -3,6 +3,7 @@ import { dict as en } from "./en"
export const dict = {
...en,
"app.meta.description": "OpenCode - L'agent de code open source.",
"nav.github": "GitHub",
"nav.docs": "Documentation",
"nav.changelog": "Changelog",
@@ -15,6 +16,7 @@ export const dict = {
"nav.home": "Accueil",
"nav.openMenu": "Ouvrir le menu",
"nav.getStartedFree": "Commencer gratuitement",
"nav.logoAlt": "OpenCode",
"nav.context.copyLogo": "Copier le logo en SVG",
"nav.context.copyWordmark": "Copier le logotype en SVG",
@@ -42,6 +44,8 @@ export const dict = {
"notFound.docs": "Documentation",
"notFound.github": "GitHub",
"notFound.discord": "Discord",
"notFound.logoLightAlt": "opencode logo light",
"notFound.logoDarkAlt": "opencode logo dark",
"user.logout": "Se déconnecter",
@@ -75,6 +79,7 @@ export const dict = {
"error.modelRequired": "Le modèle est requis",
"error.reloadAmountMin": "Le montant de recharge doit être d'au moins {{amount}} $",
"error.reloadTriggerMin": "Le seuil de déclenchement doit être d'au moins {{amount}} $",
"auth.callback.error.codeMissing": "Aucun code d'autorisation trouvé.",
"home.title": "OpenCode | L'agent de code IA open source",
@@ -91,6 +96,8 @@ export const dict = {
"temp.feature.models.afterLink": ", y compris les modèles locaux",
"temp.screenshot.caption": "OpenCode TUI avec le thème tokyonight",
"temp.screenshot.alt": "OpenCode TUI avec le thème tokyonight",
"temp.logoLightAlt": "opencode logo light",
"temp.logoDarkAlt": "opencode logo dark",
"home.banner.badge": "Nouveau",
"home.banner.text": "Application desktop disponible en bêta",
@@ -246,6 +253,24 @@ export const dict = {
"Tous les modèles Zen sont hébergés aux États-Unis. Les fournisseurs suivent une politique de rétention zéro et n'utilisent pas vos données pour l'entraînement des modèles, avec les",
"zen.privacy.exceptionsLink": "exceptions suivantes",
"zen.api.error.rateLimitExceeded": "Limite de débit dépassée. Veuillez réessayer plus tard.",
"zen.api.error.modelNotSupported": "Modèle {{model}} non pris en charge",
"zen.api.error.modelFormatNotSupported": "Modèle {{model}} non pris en charge pour le format {{format}}",
"zen.api.error.noProviderAvailable": "Aucun fournisseur disponible",
"zen.api.error.providerNotSupported": "Fournisseur {{provider}} non pris en charge",
"zen.api.error.missingApiKey": "Clé API manquante.",
"zen.api.error.invalidApiKey": "Clé API invalide.",
"zen.api.error.subscriptionQuotaExceeded": "Quota d'abonnement dépassé. Réessayez dans {{retryIn}}.",
"zen.api.error.subscriptionQuotaExceededUseFreeModels":
"Quota d'abonnement dépassé. Vous pouvez continuer à utiliser les modèles gratuits.",
"zen.api.error.noPaymentMethod": "Aucune méthode de paiement. Ajoutez une méthode de paiement ici : {{billingUrl}}",
"zen.api.error.insufficientBalance": "Solde insuffisant. Gérez votre facturation ici : {{billingUrl}}",
"zen.api.error.workspaceMonthlyLimitReached":
"Votre espace de travail a atteint sa limite de dépense mensuelle de {{amount}} $. Gérez vos limites ici : {{billingUrl}}",
"zen.api.error.userMonthlyLimitReached":
"Vous avez atteint votre limite de dépense mensuelle de {{amount}} $. Gérez vos limites ici : {{membersUrl}}",
"zen.api.error.modelDisabled": "Le modèle est désactivé",
"black.meta.title": "OpenCode Black | Accédez aux meilleurs modèles de code au monde",
"black.meta.description": "Accédez à Claude, GPT, Gemini et plus avec les forfaits d'abonnement OpenCode Black.",
"black.hero.title": "Accédez aux meilleurs modèles de code au monde",
@@ -457,6 +482,7 @@ export const dict = {
"workspace.reload.updatePaymentMethod": "Veuillez mettre à jour votre méthode de paiement et réessayer.",
"workspace.reload.retrying": "Nouvelle tentative...",
"workspace.reload.retry": "Réessayer",
"workspace.reload.error.paymentFailed": "Échec du paiement.",
"workspace.payments.title": "Historique des paiements",
"workspace.payments.subtitle": "Transactions de paiement récentes.",
@@ -581,6 +607,10 @@ export const dict = {
"enterprise.form.send": "Envoyer",
"enterprise.form.sending": "Envoi...",
"enterprise.form.success": "Message envoyé, nous vous contacterons bientôt.",
"enterprise.form.success.submitted": "Formulaire soumis avec succès.",
"enterprise.form.error.allFieldsRequired": "Tous les champs sont requis.",
"enterprise.form.error.invalidEmailFormat": "Format d'e-mail invalide.",
"enterprise.form.error.internalServer": "Erreur interne du serveur.",
"enterprise.faq.title": "FAQ",
"enterprise.faq.q1": "Qu'est-ce que OpenCode Enterprise ?",
"enterprise.faq.a1":
@@ -640,4 +670,5 @@ export const dict = {
"bench.detail.table.duration": "Durée",
"bench.detail.run.title": "Exécution {{n}}",
"bench.detail.rawJson": "JSON brut",
"bench.submission.error.allFieldsRequired": "Tous les champs sont requis.",
} satisfies Dict

View File

@@ -15,6 +15,7 @@ export const dict = {
"nav.home": "Home",
"nav.openMenu": "Apri menu",
"nav.getStartedFree": "Inizia gratis",
"nav.logoAlt": "OpenCode",
"nav.context.copyLogo": "Copia il logo come SVG",
"nav.context.copyWordmark": "Copia il wordmark come SVG",
@@ -42,9 +43,13 @@ export const dict = {
"notFound.docs": "Documentazione",
"notFound.github": "GitHub",
"notFound.discord": "Discord",
"notFound.logoLightAlt": "logo chiaro di opencode",
"notFound.logoDarkAlt": "logo scuro di opencode",
"user.logout": "Esci",
"auth.callback.error.codeMissing": "Nessun codice di autorizzazione trovato.",
"workspace.select": "Seleziona workspace",
"workspace.createNew": "+ Crea nuovo workspace",
"workspace.modal.title": "Crea nuovo workspace",
@@ -76,6 +81,8 @@ export const dict = {
"error.reloadAmountMin": "L'importo della ricarica deve essere almeno ${{amount}}",
"error.reloadTriggerMin": "La soglia del saldo deve essere almeno ${{amount}}",
"app.meta.description": "OpenCode - L'agente di programmazione open source.",
"home.title": "OpenCode | L'agente di coding IA open source",
"temp.title": "opencode | Agente di coding IA costruito per il terminale",
@@ -91,6 +98,8 @@ export const dict = {
"temp.feature.models.afterLink": ", inclusi modelli locali",
"temp.screenshot.caption": "OpenCode TUI con il tema tokyonight",
"temp.screenshot.alt": "OpenCode TUI con tema tokyonight",
"temp.logoLightAlt": "logo chiaro di opencode",
"temp.logoDarkAlt": "logo scuro di opencode",
"home.banner.badge": "Nuovo",
"home.banner.text": "App desktop disponibile in beta",
@@ -240,6 +249,24 @@ export const dict = {
"Tutti i modelli Zen sono ospitati negli Stati Uniti. I provider seguono una policy di zero-retention e non usano i tuoi dati per l'addestramento dei modelli, con le",
"zen.privacy.exceptionsLink": "seguenti eccezioni",
"zen.api.error.rateLimitExceeded": "Limite di richieste superato. Riprova più tardi.",
"zen.api.error.modelNotSupported": "Modello {{model}} non supportato",
"zen.api.error.modelFormatNotSupported": "Modello {{model}} non supportato per il formato {{format}}",
"zen.api.error.noProviderAvailable": "Nessun provider disponibile",
"zen.api.error.providerNotSupported": "Provider {{provider}} non supportato",
"zen.api.error.missingApiKey": "Chiave API mancante.",
"zen.api.error.invalidApiKey": "Chiave API non valida.",
"zen.api.error.subscriptionQuotaExceeded": "Quota dell'abbonamento superata. Riprova tra {{retryIn}}.",
"zen.api.error.subscriptionQuotaExceededUseFreeModels":
"Quota dell'abbonamento superata. Puoi continuare a utilizzare modelli gratuiti.",
"zen.api.error.noPaymentMethod": "Nessun metodo di pagamento. Aggiungi un metodo di pagamento qui: {{billingUrl}}",
"zen.api.error.insufficientBalance": "Saldo insufficiente. Gestisci la tua fatturazione qui: {{billingUrl}}",
"zen.api.error.workspaceMonthlyLimitReached":
"La tua area di lavoro ha raggiunto il limite di spesa mensile di ${{amount}}. Gestisci i tuoi limiti qui: {{billingUrl}}",
"zen.api.error.userMonthlyLimitReached":
"Hai raggiunto il tuo limite di spesa mensile di ${{amount}}. Gestisci i tuoi limiti qui: {{membersUrl}}",
"zen.api.error.modelDisabled": "Il modello è disabilitato",
"black.meta.title": "OpenCode Black | Accedi ai migliori modelli di coding al mondo",
"black.meta.description":
"Ottieni l'accesso a Claude, GPT, Gemini e altri con i piani di abbonamento OpenCode Black.",
@@ -451,6 +478,7 @@ export const dict = {
"workspace.reload.updatePaymentMethod": "Aggiorna il tuo metodo di pagamento e riprova.",
"workspace.reload.retrying": "Riprovo...",
"workspace.reload.retry": "Riprova",
"workspace.reload.error.paymentFailed": "Pagamento fallito.",
"workspace.payments.title": "Cronologia Pagamenti",
"workspace.payments.subtitle": "Transazioni di pagamento recenti.",
@@ -569,6 +597,10 @@ export const dict = {
"enterprise.form.send": "Invia",
"enterprise.form.sending": "Invio...",
"enterprise.form.success": "Messaggio inviato, ti contatteremo presto.",
"enterprise.form.success.submitted": "Modulo inviato con successo.",
"enterprise.form.error.allFieldsRequired": "Tutti i campi sono obbligatori.",
"enterprise.form.error.invalidEmailFormat": "Formato email non valido.",
"enterprise.form.error.internalServer": "Errore interno del server.",
"enterprise.faq.title": "FAQ",
"enterprise.faq.q1": "Cos'è OpenCode Enterprise?",
"enterprise.faq.a1":
@@ -601,6 +633,7 @@ export const dict = {
"bench.list.table.agent": "Agente",
"bench.list.table.model": "Modello",
"bench.list.table.score": "Punteggio",
"bench.submission.error.allFieldsRequired": "Tutti i campi sono obbligatori.",
"bench.detail.title": "Benchmark - {{task}}",
"bench.detail.notFound": "Task non trovato",

View File

@@ -15,6 +15,7 @@ export const dict = {
"nav.home": "ホーム",
"nav.openMenu": "メニューを開く",
"nav.getStartedFree": "無料ではじめる",
"nav.logoAlt": "OpenCode",
"nav.context.copyLogo": "ロゴをSVGでコピー",
"nav.context.copyWordmark": "ワードマークをSVGでコピー",
@@ -42,9 +43,13 @@ export const dict = {
"notFound.docs": "ドキュメント",
"notFound.github": "GitHub",
"notFound.discord": "Discord",
"notFound.logoLightAlt": "opencodeのロゴライト",
"notFound.logoDarkAlt": "opencodeのロゴダーク",
"user.logout": "ログアウト",
"auth.callback.error.codeMissing": "認証コードが見つかりません。",
"workspace.select": "ワークスペースを選択",
"workspace.createNew": "+ 新しいワークスペースを作成",
"workspace.modal.title": "新しいワークスペースを作成",
@@ -76,6 +81,8 @@ export const dict = {
"error.reloadAmountMin": "リロード額は少なくとも ${{amount}} である必要があります",
"error.reloadTriggerMin": "残高トリガーは少なくとも ${{amount}} である必要があります",
"app.meta.description": "OpenCode - オープンソースのコーディングエージェント。",
"home.title": "OpenCode | オープンソースのAIコーディングエージェント",
"temp.title": "OpenCode | ターミナル向けに構築されたAIコーディングエージェント",
@@ -91,6 +98,8 @@ export const dict = {
"temp.feature.models.afterLink": "を通じて75以上のLLMプロバイダーをサポート",
"temp.screenshot.caption": "tokyonight テーマを使用した OpenCode TUI",
"temp.screenshot.alt": "tokyonight テーマの OpenCode TUI",
"temp.logoLightAlt": "opencodeのロゴライト",
"temp.logoDarkAlt": "opencodeのロゴダーク",
"home.banner.badge": "新着",
"home.banner.text": "デスクトップアプリのベータ版が利用可能",
@@ -239,6 +248,25 @@ export const dict = {
"すべてのZenモデルは米国でホストされています。プロバイダーはゼロ保持ポリシーに従い、モデルのトレーニングにデータを使用しません",
"zen.privacy.exceptionsLink": "以下の例外",
"zen.api.error.rateLimitExceeded": "レート制限を超えました。後でもう一度お試しください。",
"zen.api.error.modelNotSupported": "モデル {{model}} はサポートされていません",
"zen.api.error.modelFormatNotSupported": "フォーマット {{format}} ではモデル {{model}} はサポートされていません",
"zen.api.error.noProviderAvailable": "利用可能なプロバイダーがありません",
"zen.api.error.providerNotSupported": "プロバイダー {{provider}} はサポートされていません",
"zen.api.error.missingApiKey": "APIキーがありません。",
"zen.api.error.invalidApiKey": "無効なAPIキーです。",
"zen.api.error.subscriptionQuotaExceeded":
"サブスクリプションの制限を超えました。{{retryIn}} 後に再試行してください。",
"zen.api.error.subscriptionQuotaExceededUseFreeModels":
"サブスクリプションの制限を超えました。無料モデルは引き続きご利用いただけます。",
"zen.api.error.noPaymentMethod": "お支払い方法がありません。こちらからお支払い方法を追加してください: {{billingUrl}}",
"zen.api.error.insufficientBalance": "残高が不足しています。こちらから請求を管理してください: {{billingUrl}}",
"zen.api.error.workspaceMonthlyLimitReached":
"ワークスペースが月額の利用上限 ${{amount}} に達しました。こちらから上限を管理してください: {{billingUrl}}",
"zen.api.error.userMonthlyLimitReached":
"月額の利用上限 ${{amount}} に達しました。こちらから上限を管理してください: {{membersUrl}}",
"zen.api.error.modelDisabled": "モデルが無効です",
"black.meta.title": "OpenCode Black | 世界最高峰のコーディングモデルすべてにアクセス",
"black.meta.description": "OpenCode Black サブスクリプションプランで、Claude、GPT、Gemini などにアクセス。",
"black.hero.title": "世界最高峰のコーディングモデルすべてにアクセス",
@@ -448,6 +476,7 @@ export const dict = {
"workspace.reload.updatePaymentMethod": "支払い方法を更新して、もう一度お試しください。",
"workspace.reload.retrying": "再試行中...",
"workspace.reload.retry": "再試行",
"workspace.reload.error.paymentFailed": "支払いに失敗しました。",
"workspace.payments.title": "支払い履歴",
"workspace.payments.subtitle": "最近の支払い取引。",
@@ -568,6 +597,10 @@ export const dict = {
"enterprise.form.send": "送信",
"enterprise.form.sending": "送信中...",
"enterprise.form.success": "送信しました。まもなくご連絡いたします。",
"enterprise.form.success.submitted": "フォームが正常に送信されました。",
"enterprise.form.error.allFieldsRequired": "すべての項目は必須です。",
"enterprise.form.error.invalidEmailFormat": "無効なメール形式です。",
"enterprise.form.error.internalServer": "内部サーバーエラー。",
"enterprise.faq.title": "FAQ",
"enterprise.faq.q1": "OpenCode Enterpriseとは",
"enterprise.faq.a1":
@@ -600,6 +633,7 @@ export const dict = {
"bench.list.table.agent": "エージェント",
"bench.list.table.model": "モデル",
"bench.list.table.score": "スコア",
"bench.submission.error.allFieldsRequired": "すべての項目は必須です。",
"bench.detail.title": "ベンチマーク - {{task}}",
"bench.detail.notFound": "タスクが見つかりません",

View File

@@ -15,6 +15,7 @@ export const dict = {
"nav.home": "홈",
"nav.openMenu": "메뉴 열기",
"nav.getStartedFree": "무료로 시작하기",
"nav.logoAlt": "OpenCode",
"nav.context.copyLogo": "로고를 SVG로 복사",
"nav.context.copyWordmark": "워드마크를 SVG로 복사",
@@ -42,9 +43,13 @@ export const dict = {
"notFound.docs": "문서",
"notFound.github": "GitHub",
"notFound.discord": "Discord",
"notFound.logoLightAlt": "opencode 밝은 로고",
"notFound.logoDarkAlt": "opencode 어두운 로고",
"user.logout": "로그아웃",
"auth.callback.error.codeMissing": "인증 코드를 찾을 수 없습니다.",
"workspace.select": "워크스페이스 선택",
"workspace.createNew": "+ 새 워크스페이스 만들기",
"workspace.modal.title": "새 워크스페이스 만들기",
@@ -76,6 +81,8 @@ export const dict = {
"error.reloadAmountMin": "충전 금액은 최소 ${{amount}}이어야 합니다",
"error.reloadTriggerMin": "잔액 트리거는 최소 ${{amount}}이어야 합니다",
"app.meta.description": "OpenCode - 오픈 소스 코딩 에이전트.",
"home.title": "OpenCode | 오픈 소스 AI 코딩 에이전트",
"temp.title": "OpenCode | 터미널을 위해 만들어진 AI 코딩 에이전트",
@@ -91,6 +98,8 @@ export const dict = {
"temp.feature.models.afterLink": "를 통해 75개 이상의 LLM 제공자 지원",
"temp.screenshot.caption": "tokyonight 테마가 적용된 OpenCode TUI",
"temp.screenshot.alt": "tokyonight 테마가 적용된 OpenCode TUI",
"temp.logoLightAlt": "opencode 밝은 로고",
"temp.logoDarkAlt": "opencode 어두운 로고",
"home.banner.badge": "신규",
"home.banner.text": "데스크톱 앱 베타 버전 출시",
@@ -236,6 +245,24 @@ export const dict = {
"모든 Zen 모델은 미국에서 호스팅됩니다. 제공자들은 데이터 보존 금지 정책을 따르며 모델 학습에 데이터를 사용하지 않습니다. 단,",
"zen.privacy.exceptionsLink": "다음 예외",
"zen.api.error.rateLimitExceeded": "속도 제한을 초과했습니다. 나중에 다시 시도해 주세요.",
"zen.api.error.modelNotSupported": "{{model}} 모델은 지원되지 않습니다",
"zen.api.error.modelFormatNotSupported": "{{model}} 모델은 {{format}} 형식에 대해 지원되지 않습니다",
"zen.api.error.noProviderAvailable": "사용 가능한 제공자가 없습니다",
"zen.api.error.providerNotSupported": "{{provider}} 제공자는 지원되지 않습니다",
"zen.api.error.missingApiKey": "API 키가 누락되었습니다.",
"zen.api.error.invalidApiKey": "유효하지 않은 API 키입니다.",
"zen.api.error.subscriptionQuotaExceeded": "구독 할당량을 초과했습니다. {{retryIn}} 후 다시 시도해 주세요.",
"zen.api.error.subscriptionQuotaExceededUseFreeModels":
"구독 할당량을 초과했습니다. 무료 모델은 계속 사용할 수 있습니다.",
"zen.api.error.noPaymentMethod": "결제 수단이 없습니다. 결제 수단을 추가하세요: {{billingUrl}}",
"zen.api.error.insufficientBalance": "잔액이 부족합니다. 결제 관리를 여기서 하세요: {{billingUrl}}",
"zen.api.error.workspaceMonthlyLimitReached":
"워크스페이스의 월간 지출 한도인 ${{amount}}에 도달했습니다. 한도 관리를 여기서 하세요: {{billingUrl}}",
"zen.api.error.userMonthlyLimitReached":
"월간 지출 한도인 ${{amount}}에 도달했습니다. 한도 관리를 여기서 하세요: {{membersUrl}}",
"zen.api.error.modelDisabled": "모델이 비활성화되었습니다",
"black.meta.title": "OpenCode Black | 세계 최고의 코딩 모델에 액세스하세요",
"black.meta.description": "OpenCode Black 구독 플랜으로 Claude, GPT, Gemini 등에 액세스하세요.",
"black.hero.title": "세계 최고의 코딩 모델에 액세스하세요",
@@ -445,6 +472,7 @@ export const dict = {
"workspace.reload.updatePaymentMethod": "결제 수단을 업데이트하고 다시 시도해 주세요.",
"workspace.reload.retrying": "재시도 중...",
"workspace.reload.retry": "재시도",
"workspace.reload.error.paymentFailed": "결제에 실패했습니다.",
"workspace.payments.title": "결제 내역",
"workspace.payments.subtitle": "최근 결제 거래 내역입니다.",
@@ -562,6 +590,10 @@ export const dict = {
"enterprise.form.send": "전송",
"enterprise.form.sending": "전송 중...",
"enterprise.form.success": "메시지가 전송되었습니다. 곧 연락드리겠습니다.",
"enterprise.form.success.submitted": "양식이 성공적으로 제출되었습니다.",
"enterprise.form.error.allFieldsRequired": "모든 필드는 필수 항목입니다.",
"enterprise.form.error.invalidEmailFormat": "유효하지 않은 이메일 형식입니다.",
"enterprise.form.error.internalServer": "내부 서버 오류입니다.",
"enterprise.faq.title": "FAQ",
"enterprise.faq.q1": "OpenCode 엔터프라이즈란 무엇인가요?",
"enterprise.faq.a1":
@@ -594,6 +626,7 @@ export const dict = {
"bench.list.table.agent": "에이전트",
"bench.list.table.model": "모델",
"bench.list.table.score": "점수",
"bench.submission.error.allFieldsRequired": "모든 필드는 필수 항목입니다.",
"bench.detail.title": "벤치마크 - {{task}}",
"bench.detail.notFound": "태스크를 찾을 수 없음",

View File

@@ -15,6 +15,7 @@ export const dict = {
"nav.home": "Hjem",
"nav.openMenu": "Åpne meny",
"nav.getStartedFree": "Kom i gang gratis",
"nav.logoAlt": "OpenCode",
"nav.context.copyLogo": "Kopier logo som SVG",
"nav.context.copyWordmark": "Kopier wordmark som SVG",
@@ -42,9 +43,13 @@ export const dict = {
"notFound.docs": "Dokumentasjon",
"notFound.github": "GitHub",
"notFound.discord": "Discord",
"notFound.logoLightAlt": "opencode logo lys",
"notFound.logoDarkAlt": "opencode logo mørk",
"user.logout": "Logg ut",
"auth.callback.error.codeMissing": "Ingen autorisasjonskode funnet.",
"workspace.select": "Velg arbeidsområde",
"workspace.createNew": "+ Opprett nytt arbeidsområde",
"workspace.modal.title": "Opprett nytt arbeidsområde",
@@ -76,6 +81,8 @@ export const dict = {
"error.reloadAmountMin": "Påfyllingsbeløp må være minst ${{amount}}",
"error.reloadTriggerMin": "Saldo-trigger må være minst ${{amount}}",
"app.meta.description": "OpenCode - Den åpne kildekode kodingsagenten.",
"home.title": "OpenCode | Den åpne kildekode AI-kodingsagenten",
"temp.title": "opencode | AI-kodingsagent bygget for terminalen",
@@ -91,6 +98,8 @@ export const dict = {
"temp.feature.models.afterLink": ", inkludert lokale modeller",
"temp.screenshot.caption": "opencode TUI med tokyonight-tema",
"temp.screenshot.alt": "opencode TUI med tokyonight-tema",
"temp.logoLightAlt": "opencode logo lys",
"temp.logoDarkAlt": "opencode logo mørk",
"home.banner.badge": "Ny",
"home.banner.text": "Desktop-app tilgjengelig i beta",
@@ -240,6 +249,24 @@ export const dict = {
"Alle Zen-modeller hostes i USA. Leverandører følger en policy om null oppbevaring og bruker ikke dataene dine til modelltrening, med",
"zen.privacy.exceptionsLink": "følgende unntak",
"zen.api.error.rateLimitExceeded": "Rate limit overskredet. Vennligst prøv igjen senere.",
"zen.api.error.modelNotSupported": "Modell {{model}} støttes ikke",
"zen.api.error.modelFormatNotSupported": "Modell {{model}} støttes ikke for format {{format}}",
"zen.api.error.noProviderAvailable": "Ingen leverandør tilgjengelig",
"zen.api.error.providerNotSupported": "Leverandør {{provider}} støttes ikke",
"zen.api.error.missingApiKey": "Mangler API-nøkkel.",
"zen.api.error.invalidApiKey": "Ugyldig API-nøkkel.",
"zen.api.error.subscriptionQuotaExceeded": "Abonnementskvote overskredet. Prøv igjen om {{retryIn}}.",
"zen.api.error.subscriptionQuotaExceededUseFreeModels":
"Abonnementskvote overskredet. Du kan fortsette å bruke gratis modeller.",
"zen.api.error.noPaymentMethod": "Ingen betalingsmetode. Legg til en betalingsmetode her: {{billingUrl}}",
"zen.api.error.insufficientBalance": "Utilstrekkelig saldo. Administrer faktureringen din her: {{billingUrl}}",
"zen.api.error.workspaceMonthlyLimitReached":
"Arbeidsområdet ditt har nådd sin månedlige utgiftsgrense på ${{amount}}. Administrer grensene dine her: {{billingUrl}}",
"zen.api.error.userMonthlyLimitReached":
"Du har nådd din månedlige utgiftsgrense på ${{amount}}. Administrer grensene dine her: {{membersUrl}}",
"zen.api.error.modelDisabled": "Modellen er deaktivert",
"black.meta.title": "OpenCode Black | Få tilgang til verdens beste kodemodeller",
"black.meta.description": "Få tilgang til Claude, GPT, Gemini og mer med OpenCode Black-abonnementer.",
"black.hero.title": "Få tilgang til verdens beste kodemodeller",
@@ -449,6 +476,7 @@ export const dict = {
"workspace.reload.updatePaymentMethod": "Vennligst oppdater betalingsmetoden din og prøv på nytt.",
"workspace.reload.retrying": "Prøver på nytt...",
"workspace.reload.retry": "Prøv på nytt",
"workspace.reload.error.paymentFailed": "Betaling mislyktes.",
"workspace.payments.title": "Betalingshistorikk",
"workspace.payments.subtitle": "Nylige betalingstransaksjoner.",
@@ -567,6 +595,10 @@ export const dict = {
"enterprise.form.send": "Send",
"enterprise.form.sending": "Sender...",
"enterprise.form.success": "Melding sendt, vi tar kontakt snart.",
"enterprise.form.success.submitted": "Skjemaet ble sendt inn.",
"enterprise.form.error.allFieldsRequired": "Alle felt er obligatoriske.",
"enterprise.form.error.invalidEmailFormat": "Ugyldig e-postformat.",
"enterprise.form.error.internalServer": "Intern serverfeil.",
"enterprise.faq.title": "FAQ",
"enterprise.faq.q1": "Hva er OpenCode Enterprise?",
"enterprise.faq.a1":
@@ -599,6 +631,7 @@ export const dict = {
"bench.list.table.agent": "Agent",
"bench.list.table.model": "Modell",
"bench.list.table.score": "Poengsum",
"bench.submission.error.allFieldsRequired": "Alle felt er obligatoriske.",
"bench.detail.title": "Benchmark - {{task}}",
"bench.detail.notFound": "Oppgave ikke funnet",

View File

@@ -14,6 +14,7 @@ export const dict = {
"nav.home": "Strona główna",
"nav.openMenu": "Otwórz menu",
"nav.getStartedFree": "Zacznij za darmo",
"nav.logoAlt": "OpenCode",
"nav.context.copyLogo": "Skopiuj logo jako SVG",
"nav.context.copyWordmark": "Skopiuj logotyp jako SVG",
@@ -41,9 +42,13 @@ export const dict = {
"notFound.docs": "Dokumentacja",
"notFound.github": "GitHub",
"notFound.discord": "Discord",
"notFound.logoLightAlt": "jasne logo opencode",
"notFound.logoDarkAlt": "ciemne logo opencode",
"user.logout": "Wyloguj się",
"auth.callback.error.codeMissing": "Nie znaleziono kodu autoryzacji.",
"workspace.select": "Wybierz obszar roboczy",
"workspace.createNew": "+ Utwórz nowy obszar roboczy",
"workspace.modal.title": "Utwórz nowy obszar roboczy",
@@ -75,6 +80,8 @@ export const dict = {
"error.reloadAmountMin": "Kwota doładowania musi wynosić co najmniej ${{amount}}",
"error.reloadTriggerMin": "Próg salda musi wynosić co najmniej ${{amount}}",
"app.meta.description": "OpenCode - Otwartoźródłowy agent programistyczny.",
"home.title": "OpenCode | Open source'owy agent AI do kodowania",
"temp.title": "opencode | Agent AI do kodowania zbudowany dla terminala",
@@ -90,6 +97,8 @@ export const dict = {
"temp.feature.models.afterLink": ", w tym modele lokalne",
"temp.screenshot.caption": "OpenCode TUI z motywem tokyonight",
"temp.screenshot.alt": "OpenCode TUI z motywem tokyonight",
"temp.logoLightAlt": "jasne logo opencode",
"temp.logoDarkAlt": "ciemne logo opencode",
"home.banner.badge": "Nowość",
"home.banner.text": "Aplikacja desktopowa dostępna w wersji beta",
@@ -241,6 +250,24 @@ export const dict = {
"Wszystkie modele Zen są hostowane w USA. Dostawcy stosują politykę zerowej retencji i nie wykorzystują Twoich danych do trenowania modeli, z",
"zen.privacy.exceptionsLink": "następującymi wyjątkami",
"zen.api.error.rateLimitExceeded": "Przekroczono limit zapytań. Spróbuj ponownie później.",
"zen.api.error.modelNotSupported": "Model {{model}} nie jest obsługiwany",
"zen.api.error.modelFormatNotSupported": "Model {{model}} nie jest obsługiwany dla formatu {{format}}",
"zen.api.error.noProviderAvailable": "Brak dostępnego dostawcy",
"zen.api.error.providerNotSupported": "Dostawca {{provider}} nie jest obsługiwany",
"zen.api.error.missingApiKey": "Brak klucza API.",
"zen.api.error.invalidApiKey": "Nieprawidłowy klucz API.",
"zen.api.error.subscriptionQuotaExceeded": "Przekroczono limit subskrypcji. Spróbuj ponownie za {{retryIn}}.",
"zen.api.error.subscriptionQuotaExceededUseFreeModels":
"Przekroczono limit subskrypcji. Możesz kontynuować korzystanie z darmowych modeli.",
"zen.api.error.noPaymentMethod": "Brak metody płatności. Dodaj metodę płatności tutaj: {{billingUrl}}",
"zen.api.error.insufficientBalance": "Niewystarczające saldo. Zarządzaj swoimi płatnościami tutaj: {{billingUrl}}",
"zen.api.error.workspaceMonthlyLimitReached":
"Twoja przestrzeń robocza osiągnęła miesięczny limit wydatków w wysokości ${{amount}}. Zarządzaj swoimi limitami tutaj: {{billingUrl}}",
"zen.api.error.userMonthlyLimitReached":
"Osiągnąłeś swój miesięczny limit wydatków w wysokości ${{amount}}. Zarządzaj swoimi limitami tutaj: {{membersUrl}}",
"zen.api.error.modelDisabled": "Model jest wyłączony",
"black.meta.title": "OpenCode Black | Dostęp do najlepszych na świecie modeli kodujących",
"black.meta.description": "Uzyskaj dostęp do Claude, GPT, Gemini i innych dzięki planom subskrypcji OpenCode Black.",
"black.hero.title": "Dostęp do najlepszych na świecie modeli kodujących",
@@ -450,6 +477,7 @@ export const dict = {
"workspace.reload.updatePaymentMethod": "Zaktualizuj metodę płatności i spróbuj ponownie.",
"workspace.reload.retrying": "Ponawianie...",
"workspace.reload.retry": "Spróbuj ponownie",
"workspace.reload.error.paymentFailed": "Płatność nie powiodła się.",
"workspace.payments.title": "Historia płatności",
"workspace.payments.subtitle": "Ostatnie transakcje płatnicze.",
@@ -570,6 +598,10 @@ export const dict = {
"enterprise.form.send": "Wyślij",
"enterprise.form.sending": "Wysyłanie...",
"enterprise.form.success": "Wiadomość wysłana, skontaktujemy się wkrótce.",
"enterprise.form.success.submitted": "Formularz został pomyślnie wysłany.",
"enterprise.form.error.allFieldsRequired": "Wszystkie pola są wymagane.",
"enterprise.form.error.invalidEmailFormat": "Nieprawidłowy format adresu e-mail.",
"enterprise.form.error.internalServer": "Wewnętrzny błąd serwera.",
"enterprise.faq.title": "FAQ",
"enterprise.faq.q1": "Czym jest OpenCode Enterprise?",
"enterprise.faq.a1":
@@ -602,6 +634,7 @@ export const dict = {
"bench.list.table.agent": "Agent",
"bench.list.table.model": "Model",
"bench.list.table.score": "Wynik",
"bench.submission.error.allFieldsRequired": "Wszystkie pola są wymagane.",
"bench.detail.title": "Benchmark - {{task}}",
"bench.detail.notFound": "Nie znaleziono zadania",

View File

@@ -15,6 +15,7 @@ export const dict = {
"nav.home": "Главная",
"nav.openMenu": "Открыть меню",
"nav.getStartedFree": "Начать бесплатно",
"nav.logoAlt": "OpenCode",
"nav.context.copyLogo": "Скопировать логотип как SVG",
"nav.context.copyWordmark": "Скопировать название как SVG",
@@ -42,9 +43,13 @@ export const dict = {
"notFound.docs": "Документация",
"notFound.github": "GitHub",
"notFound.discord": "Discord",
"notFound.logoLightAlt": "светлый логотип opencode",
"notFound.logoDarkAlt": "темный логотип opencode",
"user.logout": "Выйти",
"auth.callback.error.codeMissing": "Код авторизации не найден.",
"workspace.select": "Выбрать рабочее пространство",
"workspace.createNew": "+ Создать рабочее пространство",
"workspace.modal.title": "Создать рабочее пространство",
@@ -76,6 +81,8 @@ export const dict = {
"error.reloadAmountMin": "Сумма пополнения должна быть не менее ${{amount}}",
"error.reloadTriggerMin": "Порог баланса должен быть не менее ${{amount}}",
"app.meta.description": "OpenCode - AI-агент с открытым кодом для программирования.",
"home.title": "OpenCode | AI-агент с открытым кодом для программирования",
"temp.title": "opencode | AI-агент для программирования в терминале",
@@ -91,6 +98,8 @@ export const dict = {
"temp.feature.models.afterLink": ", включая локальные модели",
"temp.screenshot.caption": "OpenCode TUI с темой tokyonight",
"temp.screenshot.alt": "OpenCode TUI с темой tokyonight",
"temp.logoLightAlt": "светлый логотип opencode",
"temp.logoDarkAlt": "темный логотип opencode",
"home.banner.badge": "Новое",
"home.banner.text": "Доступно десктопное приложение (бета)",
@@ -244,6 +253,24 @@ export const dict = {
"Все модели Zen размещены в США. Провайдеры следуют политике нулевого хранения и не используют ваши данные для обучения моделей, за",
"zen.privacy.exceptionsLink": "следующими исключениями",
"zen.api.error.rateLimitExceeded": "Превышен лимит запросов. Пожалуйста, попробуйте позже.",
"zen.api.error.modelNotSupported": "Модель {{model}} не поддерживается",
"zen.api.error.modelFormatNotSupported": "Модель {{model}} не поддерживается для формата {{format}}",
"zen.api.error.noProviderAvailable": "Нет доступных провайдеров",
"zen.api.error.providerNotSupported": "Провайдер {{provider}} не поддерживается",
"zen.api.error.missingApiKey": "Отсутствует API ключ.",
"zen.api.error.invalidApiKey": "Неверный API ключ.",
"zen.api.error.subscriptionQuotaExceeded": "Квота подписки превышена. Повторите попытку через {{retryIn}}.",
"zen.api.error.subscriptionQuotaExceededUseFreeModels":
"Квота подписки превышена. Вы можете продолжить использовать бесплатные модели.",
"zen.api.error.noPaymentMethod": "Нет способа оплаты. Добавьте способ оплаты здесь: {{billingUrl}}",
"zen.api.error.insufficientBalance": "Недостаточно средств. Управляйте оплатой здесь: {{billingUrl}}",
"zen.api.error.workspaceMonthlyLimitReached":
"Ваше рабочее пространство достигло ежемесячного лимита расходов в ${{amount}}. Управляйте лимитами здесь: {{billingUrl}}",
"zen.api.error.userMonthlyLimitReached":
"Вы достигли ежемесячного лимита расходов в ${{amount}}. Управляйте лимитами здесь: {{membersUrl}}",
"zen.api.error.modelDisabled": "Модель отключена",
"black.meta.title": "OpenCode Black | Доступ к лучшим моделям для кодинга в мире",
"black.meta.description": "Получите доступ к Claude, GPT, Gemini и другим моделям с подпиской OpenCode Black.",
"black.hero.title": "Доступ к лучшим моделям для кодинга в мире",
@@ -455,6 +482,7 @@ export const dict = {
"workspace.reload.updatePaymentMethod": "Пожалуйста, обновите способ оплаты и попробуйте снова.",
"workspace.reload.retrying": "Повторная попытка...",
"workspace.reload.retry": "Повторить",
"workspace.reload.error.paymentFailed": "Ошибка оплаты.",
"workspace.payments.title": "История платежей",
"workspace.payments.subtitle": "Недавние транзакции.",
@@ -574,6 +602,10 @@ export const dict = {
"enterprise.form.send": "Отправить",
"enterprise.form.sending": "Отправка...",
"enterprise.form.success": "Сообщение отправлено, мы скоро свяжемся с вами.",
"enterprise.form.success.submitted": "Форма успешно отправлена.",
"enterprise.form.error.allFieldsRequired": "Все поля обязательны.",
"enterprise.form.error.invalidEmailFormat": "Неверный формат email.",
"enterprise.form.error.internalServer": "Внутренняя ошибка сервера.",
"enterprise.faq.title": "FAQ",
"enterprise.faq.q1": "Что такое OpenCode Enterprise?",
"enterprise.faq.a1":
@@ -606,6 +638,7 @@ export const dict = {
"bench.list.table.agent": "Агент",
"bench.list.table.model": "Модель",
"bench.list.table.score": "Оценка",
"bench.submission.error.allFieldsRequired": "Все поля обязательны.",
"bench.detail.title": "Бенчмарк - {{task}}",
"bench.detail.notFound": "Задача не найдена",

View File

@@ -15,6 +15,7 @@ export const dict = {
"nav.home": "หน้าหลัก",
"nav.openMenu": "เปิดเมนู",
"nav.getStartedFree": "เริ่มต้นฟรี",
"nav.logoAlt": "OpenCode",
"nav.context.copyLogo": "คัดลอกโลโก้เป็น SVG",
"nav.context.copyWordmark": "คัดลอกตัวอักษรแบรนด์เป็น SVG",
@@ -42,9 +43,13 @@ export const dict = {
"notFound.docs": "เอกสาร",
"notFound.github": "GitHub",
"notFound.discord": "Discord",
"notFound.logoLightAlt": "โลโก้ opencode แบบสว่าง",
"notFound.logoDarkAlt": "โลโก้ opencode แบบมืด",
"user.logout": "ออกจากระบบ",
"auth.callback.error.codeMissing": "ไม่พบ authorization code",
"workspace.select": "เลือก Workspace",
"workspace.createNew": "+ สร้าง Workspace ใหม่",
"workspace.modal.title": "สร้าง Workspace ใหม่",
@@ -76,6 +81,8 @@ export const dict = {
"error.reloadAmountMin": "จำนวนเงินที่โหลดซ้ำต้องมีอย่างน้อย ${{amount}}",
"error.reloadTriggerMin": "ยอดคงเหลือที่กระตุ้นต้องมีอย่างน้อย ${{amount}}",
"app.meta.description": "OpenCode - เอเจนต์เขียนโค้ดแบบโอเพนซอร์ส",
"home.title": "OpenCode | เอเจนต์เขียนโค้ดด้วย AI แบบโอเพนซอร์ส",
"temp.title": "OpenCode | เอเจนต์เขียนโค้ด AI ที่สร้างมาเพื่อเทอร์มินัล",
@@ -91,6 +98,8 @@ export const dict = {
"temp.feature.models.afterLink": "รวมถึงโมเดล Local",
"temp.screenshot.caption": "OpenCode TUI พร้อมธีม tokyonight",
"temp.screenshot.alt": "OpenCode TUI พร้อมธีม tokyonight",
"temp.logoLightAlt": "โลโก้ opencode แบบสว่าง",
"temp.logoDarkAlt": "โลโก้ opencode แบบมืด",
"home.banner.badge": "ใหม่",
"home.banner.text": "แอปเดสก์ท็อปพร้อมใช้งานในเวอร์ชันเบต้า",
@@ -239,6 +248,24 @@ export const dict = {
"โมเดล Zen ทั้งหมดโฮสต์ในสหรัฐอเมริกา ผู้ให้บริการปฏิบัติตามนโยบายไม่เก็บรักษาข้อมูล (zero-retention policy) และไม่ใช้ข้อมูลของคุณสำหรับการฝึกโมเดล โดยมี",
"zen.privacy.exceptionsLink": "ข้อยกเว้นดังนี้",
"zen.api.error.rateLimitExceeded": "เกินขีดจำกัดอัตราการใช้งาน กรุณาลองใหม่ในภายหลัง",
"zen.api.error.modelNotSupported": "ไม่รองรับโมเดล {{model}}",
"zen.api.error.modelFormatNotSupported": "ไม่รองรับโมเดล {{model}} สำหรับรูปแบบ {{format}}",
"zen.api.error.noProviderAvailable": "ไม่มีผู้ให้บริการที่พร้อมใช้งาน",
"zen.api.error.providerNotSupported": "ไม่รองรับผู้ให้บริการ {{provider}}",
"zen.api.error.missingApiKey": "ไม่มี API key",
"zen.api.error.invalidApiKey": "API key ไม่ถูกต้อง",
"zen.api.error.subscriptionQuotaExceeded": "โควต้าการสมัครสมาชิกเกินขีดจำกัด ลองใหม่ในอีก {{retryIn}}",
"zen.api.error.subscriptionQuotaExceededUseFreeModels":
"โควต้าการสมัครสมาชิกเกินขีดจำกัด คุณสามารถดำเนินการต่อโดยใช้โมเดลฟรี",
"zen.api.error.noPaymentMethod": "ไม่มีวิธีการชำระเงิน เพิ่มวิธีการชำระเงินที่นี่: {{billingUrl}}",
"zen.api.error.insufficientBalance": "ยอดเงินคงเหลือไม่เพียงพอ จัดการการเรียกเก็บเงินของคุณที่นี่: {{billingUrl}}",
"zen.api.error.workspaceMonthlyLimitReached":
"Workspace ของคุณถึงขีดจำกัดการใช้จ่ายรายเดือนที่ ${{amount}} แล้ว จัดการขีดจำกัดของคุณที่นี่: {{billingUrl}}",
"zen.api.error.userMonthlyLimitReached":
"คุณถึงขีดจำกัดการใช้จ่ายรายเดือนที่ ${{amount}} แล้ว จัดการขีดจำกัดของคุณที่นี่: {{membersUrl}}",
"zen.api.error.modelDisabled": "โมเดลถูกปิดใช้งาน",
"black.meta.title": "OpenCode Black | เข้าถึงโมเดลเขียนโค้ดที่ดีที่สุดในโลก",
"black.meta.description": "เข้าถึง Claude, GPT, Gemini และอื่นๆ ด้วยแผนสมาชิก OpenCode Black",
"black.hero.title": "เข้าถึงโมเดลเขียนโค้ดที่ดีที่สุดในโลก",
@@ -448,6 +475,7 @@ export const dict = {
"workspace.reload.updatePaymentMethod": "โปรดอัปเดตวิธีการชำระเงินของคุณแล้วลองอีกครั้ง",
"workspace.reload.retrying": "กำลังลองอีกครั้ง...",
"workspace.reload.retry": "ลองอีกครั้ง",
"workspace.reload.error.paymentFailed": "การชำระเงินล้มเหลว",
"workspace.payments.title": "ประวัติการชำระเงิน",
"workspace.payments.subtitle": "รายการธุรกรรมการชำระเงินล่าสุด",
@@ -566,6 +594,10 @@ export const dict = {
"enterprise.form.send": "ส่ง",
"enterprise.form.sending": "กำลังส่ง...",
"enterprise.form.success": "ส่งข้อความแล้ว เราจะติดต่อกลับเร็วๆ นี้",
"enterprise.form.success.submitted": "ส่งแบบฟอร์มสำเร็จแล้ว",
"enterprise.form.error.allFieldsRequired": "จำเป็นต้องกรอกทุกช่อง",
"enterprise.form.error.invalidEmailFormat": "รูปแบบอีเมลไม่ถูกต้อง",
"enterprise.form.error.internalServer": "เกิดข้อผิดพลาดภายในเซิร์ฟเวอร์",
"enterprise.faq.title": "คำถามที่พบบ่อย",
"enterprise.faq.q1": "OpenCode Enterprise คืออะไร?",
"enterprise.faq.a1":
@@ -598,6 +630,7 @@ export const dict = {
"bench.list.table.agent": "เอเจนต์",
"bench.list.table.model": "โมเดล",
"bench.list.table.score": "คะแนน",
"bench.submission.error.allFieldsRequired": "จำเป็นต้องกรอกทุกช่อง",
"bench.detail.title": "Benchmark - {{task}}",
"bench.detail.notFound": "ไม่พบงาน",

View File

@@ -15,6 +15,7 @@ export const dict = {
"nav.home": "Ana sayfa",
"nav.openMenu": "Menüyü aç",
"nav.getStartedFree": "Ücretsiz başla",
"nav.logoAlt": "OpenCode",
"nav.context.copyLogo": "Logoyu SVG olarak kopyala",
"nav.context.copyWordmark": "Wordmark'ı SVG olarak kopyala",
@@ -42,9 +43,13 @@ export const dict = {
"notFound.docs": "Dokümantasyon",
"notFound.github": "GitHub",
"notFound.discord": "Discord",
"notFound.logoLightAlt": "opencode açık logo",
"notFound.logoDarkAlt": "opencode koyu logo",
"user.logout": ıkış",
"auth.callback.error.codeMissing": "Yetkilendirme kodu bulunamadı.",
"workspace.select": "Çalışma alanı seç",
"workspace.createNew": "+ Yeni çalışma alanı oluştur",
"workspace.modal.title": "Yeni çalışma alanı oluştur",
@@ -76,6 +81,8 @@ export const dict = {
"error.reloadAmountMin": "Yükleme tutarı en az ${{amount}} olmalıdır",
"error.reloadTriggerMin": "Bakiye tetikleyicisi en az ${{amount}} olmalıdır",
"app.meta.description": "OpenCode - Açık kaynaklı kodlama ajanı.",
"home.title": "OpenCode | Açık kaynaklı yapay zeka kodlama ajanı",
"temp.title": "opencode | Terminal için geliştirilmiş yapay zeka kodlama ajanı",
@@ -91,6 +98,8 @@ export const dict = {
"temp.feature.models.afterLink": " üzerinden destekler",
"temp.screenshot.caption": "opencode TUI ve tokyonight teması",
"temp.screenshot.alt": "tokyonight temalı opencode TUI",
"temp.logoLightAlt": "opencode açık logo",
"temp.logoDarkAlt": "opencode koyu logo",
"home.banner.badge": "Yeni",
"home.banner.text": "Masaüstü uygulaması beta olarak kullanılabilir",
@@ -242,6 +251,24 @@ export const dict = {
"Tüm Zen modelleri ABD'de barındırılmaktadır. Sağlayıcılar sıfır saklama politikası izler ve verilerinizi model eğitimi için kullanmaz; şu",
"zen.privacy.exceptionsLink": "aşağıdaki istisnalar",
"zen.api.error.rateLimitExceeded": "İstek limiti aşıldı. Lütfen daha sonra tekrar deneyin.",
"zen.api.error.modelNotSupported": "{{model}} modeli desteklenmiyor",
"zen.api.error.modelFormatNotSupported": "{{model}} modeli {{format}} formatı için desteklenmiyor",
"zen.api.error.noProviderAvailable": "Kullanılabilir sağlayıcı yok",
"zen.api.error.providerNotSupported": "{{provider}} sağlayıcısı desteklenmiyor",
"zen.api.error.missingApiKey": "API anahtarı eksik.",
"zen.api.error.invalidApiKey": "Geçersiz API anahtarı.",
"zen.api.error.subscriptionQuotaExceeded": "Abonelik kotasııldı. {{retryIn}} içinde tekrar deneyin.",
"zen.api.error.subscriptionQuotaExceededUseFreeModels":
"Abonelik kotasııldı. Ücretsiz modelleri kullanmaya devam edebilirsiniz.",
"zen.api.error.noPaymentMethod": "Ödeme yöntemi bulunamadı. Buradan bir ödeme yöntemi ekleyin: {{billingUrl}}",
"zen.api.error.insufficientBalance": "Yetersiz bakiye. Faturalandırmanızı buradan yönetin: {{billingUrl}}",
"zen.api.error.workspaceMonthlyLimitReached":
"Çalışma alanınız aylık ${{amount}} harcama limitine ulaştı. Limitlerinizi buradan yönetin: {{billingUrl}}",
"zen.api.error.userMonthlyLimitReached":
"Aylık ${{amount}} harcama limitinize ulaştınız. Limitlerinizi buradan yönetin: {{membersUrl}}",
"zen.api.error.modelDisabled": "Model devre dışı",
"black.meta.title": "OpenCode Black | Dünyanın en iyi kodlama modellerine erişin",
"black.meta.description": "OpenCode Black abonelik planlarıyla Claude, GPT, Gemini ve daha fazlasına erişin.",
"black.hero.title": "Dünyanın en iyi kodlama modellerine erişin",
@@ -451,6 +478,7 @@ export const dict = {
"workspace.reload.updatePaymentMethod": "Lütfen ödeme yönteminizi güncelleyin ve tekrar deneyin.",
"workspace.reload.retrying": "Yeniden deneniyor...",
"workspace.reload.retry": "Yeniden dene",
"workspace.reload.error.paymentFailed": "Ödeme başarısız.",
"workspace.payments.title": "Ödeme Geçmişi",
"workspace.payments.subtitle": "Son ödeme işlemleri.",
@@ -571,6 +599,10 @@ export const dict = {
"enterprise.form.send": "Gönder",
"enterprise.form.sending": "Gönderiliyor...",
"enterprise.form.success": "Mesaj gönderildi, yakında size dönüş yapacağız.",
"enterprise.form.success.submitted": "Form başarıyla gönderildi.",
"enterprise.form.error.allFieldsRequired": "Tüm alanlar gereklidir.",
"enterprise.form.error.invalidEmailFormat": "Geçersiz e-posta formatı.",
"enterprise.form.error.internalServer": "İç sunucu hatası.",
"enterprise.faq.title": "SSS",
"enterprise.faq.q1": "OpenCode Enterprise nedir?",
"enterprise.faq.a1":
@@ -603,6 +635,7 @@ export const dict = {
"bench.list.table.agent": "Ajan",
"bench.list.table.model": "Model",
"bench.list.table.score": "Puan",
"bench.submission.error.allFieldsRequired": "Tüm alanlar gereklidir.",
"bench.detail.title": "Benchmark - {{task}}",
"bench.detail.notFound": "Görev bulunamadı",

View File

@@ -15,6 +15,7 @@ export const dict = {
"nav.home": "首页",
"nav.openMenu": "打开菜单",
"nav.getStartedFree": "免费开始",
"nav.logoAlt": "OpenCode",
"nav.context.copyLogo": "复制 Logo (SVG)",
"nav.context.copyWordmark": "复制商标 (SVG)",
@@ -42,9 +43,13 @@ export const dict = {
"notFound.docs": "文档",
"notFound.github": "GitHub",
"notFound.discord": "Discord",
"notFound.logoLightAlt": "opencode logo 亮色",
"notFound.logoDarkAlt": "opencode logo 暗色",
"user.logout": "退出登录",
"auth.callback.error.codeMissing": "未找到授权码。",
"workspace.select": "选择工作区",
"workspace.createNew": "+ 新建工作区",
"workspace.modal.title": "新建工作区",
@@ -76,6 +81,8 @@ export const dict = {
"error.reloadAmountMin": "充值金额必须至少为 ${{amount}}",
"error.reloadTriggerMin": "余额触发阈值必须至少为 ${{amount}}",
"app.meta.description": "OpenCode - 开源编程代理。",
"home.title": "OpenCode | 开源 AI 编程代理",
"temp.title": "OpenCode | 专为终端打造的 AI 编程代理",
@@ -91,6 +98,8 @@ export const dict = {
"temp.feature.models.afterLink": ",包括本地模型",
"temp.screenshot.caption": "使用 Tokyonight 主题的 OpenCode TUI",
"temp.screenshot.alt": "使用 Tokyonight 主题的 OpenCode TUI",
"temp.logoLightAlt": "opencode logo 亮色",
"temp.logoDarkAlt": "opencode logo 暗色",
"home.banner.badge": "新",
"home.banner.text": "桌面应用 Beta 版现已推出",
@@ -229,6 +238,22 @@ export const dict = {
"zen.privacy.beforeExceptions": "所有 Zen 模型均托管在美国。提供商遵循零留存政策,不使用您的数据进行模型训练,",
"zen.privacy.exceptionsLink": "以下例外情况除外",
"zen.api.error.rateLimitExceeded": "超出速率限制。请稍后重试。",
"zen.api.error.modelNotSupported": "不支持模型 {{model}}",
"zen.api.error.modelFormatNotSupported": "格式 {{format}} 不支持模型 {{model}}",
"zen.api.error.noProviderAvailable": "没有可用的提供商",
"zen.api.error.providerNotSupported": "不支持提供商 {{provider}}",
"zen.api.error.missingApiKey": "缺少 API 密钥。",
"zen.api.error.invalidApiKey": "无效的 API 密钥。",
"zen.api.error.subscriptionQuotaExceeded": "超出订阅配额。请在 {{retryIn}} 后重试。",
"zen.api.error.subscriptionQuotaExceededUseFreeModels": "超出订阅配额。您可以继续使用免费模型。",
"zen.api.error.noPaymentMethod": "没有付款方式。请在此处添加付款方式:{{billingUrl}}",
"zen.api.error.insufficientBalance": "余额不足。请在此处管理您的计费:{{billingUrl}}",
"zen.api.error.workspaceMonthlyLimitReached":
"您的工作区已达到每月支出限额 ${{amount}}。请在此处管理您的限额:{{billingUrl}}",
"zen.api.error.userMonthlyLimitReached": "您已达到每月支出限额 ${{amount}}。请在此处管理您的限额:{{membersUrl}}",
"zen.api.error.modelDisabled": "模型已禁用",
"black.meta.title": "OpenCode Black | 访问全球顶尖编程模型",
"black.meta.description": "通过 OpenCode Black 订阅计划使用 Claude, GPT, Gemini 等模型。",
"black.hero.title": "访问全球顶尖编程模型",
@@ -436,6 +461,7 @@ export const dict = {
"workspace.reload.updatePaymentMethod": "请更新您的付款方式并重试。",
"workspace.reload.retrying": "正在重试...",
"workspace.reload.retry": "重试",
"workspace.reload.error.paymentFailed": "支付失败。",
"workspace.payments.title": "支付历史",
"workspace.payments.subtitle": "近期支付交易。",
@@ -552,6 +578,10 @@ export const dict = {
"enterprise.form.send": "发送",
"enterprise.form.sending": "正在发送...",
"enterprise.form.success": "消息已发送,我们会尽快与您联系。",
"enterprise.form.success.submitted": "表单提交成功。",
"enterprise.form.error.allFieldsRequired": "所有字段均为必填项。",
"enterprise.form.error.invalidEmailFormat": "邮箱格式无效。",
"enterprise.form.error.internalServer": "内部服务器错误。",
"enterprise.faq.title": "常见问题",
"enterprise.faq.q1": "什么是 OpenCode 企业版?",
"enterprise.faq.a1":
@@ -584,6 +614,7 @@ export const dict = {
"bench.list.table.agent": "代理",
"bench.list.table.model": "模型",
"bench.list.table.score": "分数",
"bench.submission.error.allFieldsRequired": "所有字段均为必填项。",
"bench.detail.title": "基准测试 - {{task}}",
"bench.detail.notFound": "未找到任务",

View File

@@ -15,6 +15,7 @@ export const dict = {
"nav.home": "首頁",
"nav.openMenu": "開啟選單",
"nav.getStartedFree": "免費開始使用",
"nav.logoAlt": "OpenCode",
"nav.context.copyLogo": "複製標誌SVG",
"nav.context.copyWordmark": "複製字標SVG",
@@ -42,9 +43,13 @@ export const dict = {
"notFound.docs": "文件",
"notFound.github": "GitHub",
"notFound.discord": "Discord",
"notFound.logoLightAlt": "opencode 淺色標誌",
"notFound.logoDarkAlt": "opencode 深色標誌",
"user.logout": "登出",
"auth.callback.error.codeMissing": "找不到授權碼。",
"workspace.select": "選取工作區",
"workspace.createNew": "+ 建立新工作區",
"workspace.modal.title": "建立新工作區",
@@ -76,6 +81,8 @@ export const dict = {
"error.reloadAmountMin": "儲值金額必須至少為 ${{amount}}",
"error.reloadTriggerMin": "餘額觸發門檻必須至少為 ${{amount}}",
"app.meta.description": "OpenCode - 開源編碼代理。",
"home.title": "OpenCode | 開源 AI 編碼代理",
"temp.title": "OpenCode | 專為終端打造的 AI 編碼代理",
@@ -91,6 +98,8 @@ export const dict = {
"temp.feature.models.afterLink": "支援 75+ 家 LLM 供應商,包括本地模型",
"temp.screenshot.caption": "使用 tokyonight 主題的 OpenCode TUI",
"temp.screenshot.alt": "使用 tokyonight 主題的 OpenCode TUI",
"temp.logoLightAlt": "opencode 淺色標誌",
"temp.logoDarkAlt": "opencode 深色標誌",
"home.banner.badge": "新",
"home.banner.text": "桌面應用已推出 Beta",
@@ -229,6 +238,22 @@ export const dict = {
"zen.privacy.beforeExceptions": "所有 Zen 模型均在美國託管。供應商遵循零留存政策,不會將你的資料用於模型訓練,並且有",
"zen.privacy.exceptionsLink": "以下例外情況",
"zen.api.error.rateLimitExceeded": "超出頻率限制。請稍後再試。",
"zen.api.error.modelNotSupported": "不支援模型 {{model}}",
"zen.api.error.modelFormatNotSupported": "模型 {{model}} 不支援格式 {{format}}",
"zen.api.error.noProviderAvailable": "無可用的供應商",
"zen.api.error.providerNotSupported": "不支援供應商 {{provider}}",
"zen.api.error.missingApiKey": "缺少 API 金鑰。",
"zen.api.error.invalidApiKey": "無效的 API 金鑰。",
"zen.api.error.subscriptionQuotaExceeded": "超出訂閱配額。請在 {{retryIn}} 後重試。",
"zen.api.error.subscriptionQuotaExceededUseFreeModels": "超出訂閱配額。你可以繼續使用免費模型。",
"zen.api.error.noPaymentMethod": "無付款方式。請在此處新增付款方式:{{billingUrl}}",
"zen.api.error.insufficientBalance": "餘額不足。請在此處管理你的帳務:{{billingUrl}}",
"zen.api.error.workspaceMonthlyLimitReached":
"你的工作區已達到每月支出限額 ${{amount}}。請在此處管理你的限額:{{billingUrl}}",
"zen.api.error.userMonthlyLimitReached": "你已達到每月支出限額 ${{amount}}。請在此處管理你的限額:{{membersUrl}}",
"zen.api.error.modelDisabled": "模型已停用",
"black.meta.title": "OpenCode Black | 存取全球最佳編碼模型",
"black.meta.description": "透過 OpenCode Black 訂閱方案存取 Claude、GPT、Gemini 等模型。",
"black.hero.title": "存取全球最佳編碼模型",
@@ -436,6 +461,7 @@ export const dict = {
"workspace.reload.updatePaymentMethod": "請更新你的付款方式並重試。",
"workspace.reload.retrying": "重試中...",
"workspace.reload.retry": "重試",
"workspace.reload.error.paymentFailed": "付款失敗。",
"workspace.payments.title": "付款紀錄",
"workspace.payments.subtitle": "最近的付款交易。",
@@ -551,6 +577,10 @@ export const dict = {
"enterprise.form.send": "傳送",
"enterprise.form.sending": "傳送中...",
"enterprise.form.success": "訊息已傳送,我們會盡快與你聯絡。",
"enterprise.form.success.submitted": "表單已成功送出。",
"enterprise.form.error.allFieldsRequired": "所有欄位均為必填。",
"enterprise.form.error.invalidEmailFormat": "無效的電子郵件格式。",
"enterprise.form.error.internalServer": "內部伺服器錯誤。",
"enterprise.faq.title": "常見問題",
"enterprise.faq.q1": "什麼是 OpenCode Enterprise",
"enterprise.faq.a1":
@@ -583,6 +613,7 @@ export const dict = {
"bench.list.table.agent": "代理",
"bench.list.table.model": "模型",
"bench.list.table.score": "分數",
"bench.submission.error.allFieldsRequired": "所有欄位均為必填。",
"bench.detail.title": "評測 - {{task}}",
"bench.detail.notFound": "找不到任務",

View File

@@ -48,6 +48,9 @@ const map = {
"Provider is required": "error.providerRequired",
"API key is required": "error.apiKeyRequired",
"Model is required": "error.modelRequired",
"workspace.reload.error.paymentFailed": "workspace.reload.error.paymentFailed",
"Payment failed": "workspace.reload.error.paymentFailed",
"Payment failed.": "workspace.reload.error.paymentFailed",
} as const satisfies Record<string, Key>
export function formErrorReloadAmountMin(amount: number) {

View File

@@ -16,8 +16,8 @@ export default function NotFound() {
<div data-component="content">
<section data-component="top">
<a href={language.route("/")} data-slot="logo-link">
<img data-slot="logo light" src={logoLight} alt="opencode logo light" />
<img data-slot="logo dark" src={logoDark} alt="opencode logo dark" />
<img data-slot="logo light" src={logoLight} alt={i18n.t("notFound.logoLightAlt")} />
<img data-slot="logo dark" src={logoDark} alt={i18n.t("notFound.logoDarkAlt")} />
</a>
<h1 data-slot="title">{i18n.t("notFound.heading")}</h1>
</section>

View File

@@ -1,5 +1,7 @@
import type { APIEvent } from "@solidjs/start/server"
import { AWS } from "@opencode-ai/console-core/aws.js"
import { i18n } from "~/i18n"
import { localeFromRequest } from "~/lib/language"
interface EnterpriseFormData {
name: string
@@ -9,18 +11,19 @@ interface EnterpriseFormData {
}
export async function POST(event: APIEvent) {
const dict = i18n(localeFromRequest(event.request))
try {
const body = (await event.request.json()) as EnterpriseFormData
// Validate required fields
if (!body.name || !body.role || !body.email || !body.message) {
return Response.json({ error: "All fields are required" }, { status: 400 })
return Response.json({ error: dict["enterprise.form.error.allFieldsRequired"] }, { status: 400 })
}
// Validate email format
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
if (!emailRegex.test(body.email)) {
return Response.json({ error: "Invalid email format" }, { status: 400 })
return Response.json({ error: dict["enterprise.form.error.invalidEmailFormat"] }, { status: 400 })
}
// Create email content
@@ -39,9 +42,9 @@ ${body.email}`.trim()
replyTo: body.email,
})
return Response.json({ success: true, message: "Form submitted successfully" }, { status: 200 })
return Response.json({ success: true, message: dict["enterprise.form.success.submitted"] }, { status: 200 })
} catch (error) {
console.error("Error processing enterprise form:", error)
return Response.json({ error: "Internal server error" }, { status: 500 })
return Response.json({ error: dict["enterprise.form.error.internalServer"] }, { status: 500 })
}
}

View File

@@ -2,15 +2,17 @@ import { redirect } from "@solidjs/router"
import type { APIEvent } from "@solidjs/start/server"
import { AuthClient } from "~/context/auth"
import { useAuthSession } from "~/context/auth"
import { i18n } from "~/i18n"
import { localeFromRequest, route } from "~/lib/language"
export async function GET(input: APIEvent) {
const url = new URL(input.request.url)
const locale = localeFromRequest(input.request)
const dict = i18n(locale)
try {
const code = url.searchParams.get("code")
if (!code) throw new Error("No code found")
if (!code) throw new Error(dict["auth.callback.error.codeMissing"])
const result = await AuthClient.exchange(code, `${url.origin}${url.pathname}`)
if (result.err) throw new Error(result.err.message)
const decoded = AuthClient.decode(result.tokens.access, {} as any)

View File

@@ -2,6 +2,8 @@ import type { APIEvent } from "@solidjs/start/server"
import { Database } from "@opencode-ai/console-core/drizzle/index.js"
import { BenchmarkTable } from "@opencode-ai/console-core/schema/benchmark.sql.js"
import { Identifier } from "@opencode-ai/console-core/identifier.js"
import { i18n } from "~/i18n"
import { localeFromRequest } from "~/lib/language"
interface SubmissionBody {
model: string
@@ -10,10 +12,11 @@ interface SubmissionBody {
}
export async function POST(event: APIEvent) {
const dict = i18n(localeFromRequest(event.request))
const body = (await event.request.json()) as SubmissionBody
if (!body.model || !body.agent || !body.result) {
return Response.json({ error: "All fields are required" }, { status: 400 })
return Response.json({ error: dict["bench.submission.error.allFieldsRequired"] }, { status: 400 })
}
await Database.use((tx) =>

View File

@@ -33,6 +33,7 @@ const brandAssets = "/opencode-brand-assets.zip"
export default function Brand() {
const i18n = useI18n()
const alt = i18n.t("brand.meta.description")
const downloadFile = async (url: string, filename: string) => {
try {
const response = await fetch(url)
@@ -88,7 +89,7 @@ export default function Brand() {
<div data-component="brand-grid">
<div>
<img src={previewLogoLight} alt="OpenCode brand guidelines" />
<img src={previewLogoLight} alt={alt} />
<div data-component="actions">
<button onClick={() => downloadFile(logoLightPng, "opencode-logo-light.png")}>
PNG
@@ -115,7 +116,7 @@ export default function Brand() {
</div>
</div>
<div>
<img src={previewLogoDark} alt="OpenCode brand guidelines" />
<img src={previewLogoDark} alt={alt} />
<div data-component="actions">
<button onClick={() => downloadFile(logoDarkPng, "opencode-logo-dark.png")}>
PNG
@@ -142,7 +143,7 @@ export default function Brand() {
</div>
</div>
<div>
<img src={previewLogoLightSquare} alt="OpenCode brand guidelines" />
<img src={previewLogoLightSquare} alt={alt} />
<div data-component="actions">
<button onClick={() => downloadFile(logoLightSquarePng, "opencode-logo-light-square.png")}>
PNG
@@ -169,7 +170,7 @@ export default function Brand() {
</div>
</div>
<div>
<img src={previewLogoDarkSquare} alt="OpenCode brand guidelines" />
<img src={previewLogoDarkSquare} alt={alt} />
<div data-component="actions">
<button onClick={() => downloadFile(logoDarkSquarePng, "opencode-logo-dark-square.png")}>
PNG
@@ -196,7 +197,7 @@ export default function Brand() {
</div>
</div>
<div>
<img src={previewWordmarkLight} alt="OpenCode brand guidelines" />
<img src={previewWordmarkLight} alt={alt} />
<div data-component="actions">
<button onClick={() => downloadFile(wordmarkLightPng, "opencode-wordmark-light.png")}>
PNG
@@ -223,7 +224,7 @@ export default function Brand() {
</div>
</div>
<div>
<img src={previewWordmarkDark} alt="OpenCode brand guidelines" />
<img src={previewWordmarkDark} alt={alt} />
<div data-component="actions">
<button onClick={() => downloadFile(wordmarkDarkPng, "opencode-wordmark-dark.png")}>
PNG
@@ -250,7 +251,7 @@ export default function Brand() {
</div>
</div>
<div>
<img src={previewWordmarkSimpleLight} alt="OpenCode brand guidelines" />
<img src={previewWordmarkSimpleLight} alt={alt} />
<div data-component="actions">
<button onClick={() => downloadFile(wordmarkSimpleLightPng, "opencode-wordmark-simple-light.png")}>
PNG
@@ -277,7 +278,7 @@ export default function Brand() {
</div>
</div>
<div>
<img src={previewWordmarkSimpleDark} alt="OpenCode brand guidelines" />
<img src={previewWordmarkSimpleDark} alt={alt} />
<div data-component="actions">
<button onClick={() => downloadFile(wordmarkSimpleDarkPng, "opencode-wordmark-simple-dark.png")}>
PNG

View File

@@ -19,7 +19,7 @@ const downloadNames: Record<string, string> = {
export async function GET({ params: { platform, channel } }: APIEvent) {
const assetName = assetNames[platform]
if (!assetName) return new Response("Not Found", { status: 404 })
if (!assetName) return new Response(null, { status: 404 })
const resp = await fetch(
`https://github.com/anomalyco/${channel === "stable" ? "opencode" : "opencode-beta"}/releases/latest/download/${assetName}`,

View File

@@ -306,7 +306,7 @@ export async function POST(input: APIEvent) {
.update(BillingTable)
.set({
reload: false,
reloadError: errorMessage ?? "Payment failed.",
reloadError: errorMessage ?? "workspace.reload.error.paymentFailed",
timeReloadError: sql`now()`,
})
.where(eq(BillingTable.workspaceID, Actor.workspace())),

View File

@@ -47,8 +47,8 @@ export default function Home() {
<div data-component="content">
<section data-component="top">
<img data-slot="logo light" src={logoLight} alt="opencode logo light" />
<img data-slot="logo dark" src={logoDark} alt="opencode logo dark" />
<img data-slot="logo light" src={logoLight} alt={i18n.t("temp.logoLightAlt")} />
<img data-slot="logo dark" src={logoDark} alt={i18n.t("temp.logoDarkAlt")} />
<h1 data-slot="title">{i18n.t("temp.hero.title")}</h1>
<div data-slot="login">
<a href="/auth">{i18n.t("temp.zen")}</a>

View File

@@ -12,6 +12,7 @@ import { queryBillingInfo } from "../../common"
import styles from "./lite-section.module.css"
import { useI18n } from "~/context/i18n"
import { useLanguage } from "~/context/language"
import { formError } from "~/lib/form-error"
const queryLiteSubscription = query(async (workspaceID: string) => {
"use server"
@@ -114,7 +115,7 @@ const createSessionUrl = action(async (workspaceID: string, returnUrl: string) =
const setLiteUseBalance = action(async (form: FormData) => {
"use server"
const workspaceID = form.get("workspaceID")?.toString()
if (!workspaceID) return { error: "Workspace ID is required" }
if (!workspaceID) return { error: formError.workspaceRequired }
const useBalance = form.get("useBalance")?.toString() === "true"
return json(

View File

@@ -202,7 +202,8 @@ export function ReloadSection() {
minute: "2-digit",
second: "2-digit",
})}
. {i18n.t("workspace.reload.reason")} {billingInfo()?.reloadError?.replace(/\.$/, "")}.{" "}
. {i18n.t("workspace.reload.reason")}{" "}
{localizeError(i18n.t, billingInfo()?.reloadError ?? undefined).replace(/\.$/, "")}.{" "}
{i18n.t("workspace.reload.updatePaymentMethod")}
</p>
<form action={reload} method="post" data-slot="create-form">

View File

@@ -35,6 +35,8 @@ import { createTrialLimiter } from "./trialLimiter"
import { createStickyTracker } from "./stickyProviderTracker"
import { LiteData } from "@opencode-ai/console-core/lite.js"
import { Resource } from "@opencode-ai/console-resource"
import { i18n, type Key } from "~/i18n"
import { localeFromRequest } from "~/lib/language"
type ZenData = Awaited<ReturnType<typeof ZenData.list>>
type RetryOptions = {
@@ -43,6 +45,15 @@ type RetryOptions = {
}
type BillingSource = "anonymous" | "free" | "byok" | "subscription" | "lite" | "balance"
function resolve(text: string, params?: Record<string, string | number>) {
if (!params) return text
return text.replace(/\{\{(\w+)\}\}/g, (raw, key) => {
const value = params[key]
if (value === undefined || value === null) return raw
return String(value)
})
}
export async function handler(
input: APIEvent,
opts: {
@@ -60,6 +71,8 @@ export async function handler(
const MAX_FAILOVER_RETRIES = 3
const MAX_429_RETRIES = 3
const dict = i18n(localeFromRequest(input.request))
const t = (key: Key, params?: Record<string, string | number>) => resolve(dict[key], params)
const ADMIN_WORKSPACES = [
"wrk_01K46JDFR0E75SG2Q8K172KF3Y", // frank
"wrk_01K6W1A3VE0KMNVSCQT43BG2SX", // opencode bench
@@ -86,7 +99,7 @@ export async function handler(
const dataDumper = createDataDumper(sessionId, requestId, projectId)
const trialLimiter = createTrialLimiter(modelInfo.trial, ip, ocClient)
const isTrial = await trialLimiter?.isTrial()
const rateLimiter = createRateLimiter(modelInfo.rateLimit, ip, input.request.headers)
const rateLimiter = createRateLimiter(modelInfo.rateLimit, ip, input.request)
await rateLimiter?.check()
const stickyTracker = createStickyTracker(modelInfo.stickyProvider, sessionId)
const stickyProvider = await stickyTracker?.get()
@@ -359,14 +372,20 @@ export async function handler(
}
function validateModel(zenData: ZenData, reqModel: string) {
if (!(reqModel in zenData.models)) throw new ModelError(`Model ${reqModel} not supported`)
if (!(reqModel in zenData.models)) throw new ModelError(t("zen.api.error.modelNotSupported", { model: reqModel }))
const modelId = reqModel as keyof typeof zenData.models
const modelData = Array.isArray(zenData.models[modelId])
? zenData.models[modelId].find((model) => opts.format === model.formatFilter)
: zenData.models[modelId]
if (!modelData) throw new ModelError(`Model ${reqModel} not supported for format ${opts.format}`)
if (!modelData)
throw new ModelError(
t("zen.api.error.modelFormatNotSupported", {
model: reqModel,
format: opts.format,
}),
)
logger.metric({ model: modelId })
@@ -418,8 +437,9 @@ export async function handler(
return modelInfo.providers.find((provider) => provider.id === modelInfo.fallbackProvider)
})()
if (!modelProvider) throw new ModelError("No provider available")
if (!(modelProvider.id in zenData.providers)) throw new ModelError(`Provider ${modelProvider.id} not supported`)
if (!modelProvider) throw new ModelError(t("zen.api.error.noProviderAvailable"))
if (!(modelProvider.id in zenData.providers))
throw new ModelError(t("zen.api.error.providerNotSupported", { provider: modelProvider.id }))
return {
...modelProvider,
@@ -439,7 +459,7 @@ export async function handler(
const apiKey = opts.parseApiKey(input.request.headers)
if (!apiKey || apiKey === "public") {
if (modelInfo.allowAnonymous) return
throw new AuthError("Missing API key.")
throw new AuthError(t("zen.api.error.missingApiKey"))
}
const data = await Database.use((tx) =>
@@ -520,13 +540,13 @@ export async function handler(
.then((rows) => rows[0]),
)
if (!data) throw new AuthError("Invalid API key.")
if (!data) throw new AuthError(t("zen.api.error.invalidApiKey"))
if (
modelInfo.id.startsWith("alpha-") &&
Resource.App.stage === "production" &&
!ADMIN_WORKSPACES.includes(data.workspaceID)
)
throw new AuthError(`Model ${modelInfo.id} not supported`)
throw new AuthError(t("zen.api.error.modelNotSupported", { model: modelInfo.id }))
logger.metric({
api_key: data.apiKey,
@@ -590,7 +610,9 @@ export async function handler(
})
if (result.status === "rate-limited")
throw new SubscriptionUsageLimitError(
`Subscription quota exceeded. Retry in ${formatRetryTime(result.resetInSec)}.`,
t("zen.api.error.subscriptionQuotaExceeded", {
retryIn: formatRetryTime(result.resetInSec),
}),
result.resetInSec,
)
}
@@ -606,7 +628,9 @@ export async function handler(
})
if (result.status === "rate-limited")
throw new SubscriptionUsageLimitError(
`Subscription quota exceeded. Retry in ${formatRetryTime(result.resetInSec)}.`,
t("zen.api.error.subscriptionQuotaExceeded", {
retryIn: formatRetryTime(result.resetInSec),
}),
result.resetInSec,
)
}
@@ -632,7 +656,7 @@ export async function handler(
})
if (result.status === "rate-limited")
throw new SubscriptionUsageLimitError(
`Subscription quota exceeded. Retry in ${formatRetryTime(result.resetInSec)}.`,
t("zen.api.error.subscriptionQuotaExceededUseFreeModels"),
result.resetInSec,
)
}
@@ -647,7 +671,7 @@ export async function handler(
})
if (result.status === "rate-limited")
throw new SubscriptionUsageLimitError(
`Subscription quota exceeded. Retry in ${formatRetryTime(result.resetInSec)}.`,
t("zen.api.error.subscriptionQuotaExceededUseFreeModels"),
result.resetInSec,
)
}
@@ -662,7 +686,7 @@ export async function handler(
})
if (result.status === "rate-limited")
throw new SubscriptionUsageLimitError(
`Subscription quota exceeded. Retry in ${formatRetryTime(result.resetInSec)}.`,
t("zen.api.error.subscriptionQuotaExceededUseFreeModels"),
result.resetInSec,
)
}
@@ -675,14 +699,10 @@ export async function handler(
// Validate pay as you go billing
const billing = authInfo.billing
if (!billing.paymentMethodID)
throw new CreditsError(
`No payment method. Add a payment method here: https://opencode.ai/workspace/${authInfo.workspaceID}/billing`,
)
if (billing.balance <= 0)
throw new CreditsError(
`Insufficient balance. Manage your billing here: https://opencode.ai/workspace/${authInfo.workspaceID}/billing`,
)
const billingUrl = `https://opencode.ai/workspace/${authInfo.workspaceID}/billing`
const membersUrl = `https://opencode.ai/workspace/${authInfo.workspaceID}/members`
if (!billing.paymentMethodID) throw new CreditsError(t("zen.api.error.noPaymentMethod", { billingUrl }))
if (billing.balance <= 0) throw new CreditsError(t("zen.api.error.insufficientBalance", { billingUrl }))
const now = new Date()
const currentYear = now.getUTCFullYear()
@@ -696,7 +716,10 @@ export async function handler(
currentMonth === billing.timeMonthlyUsageUpdated.getUTCMonth()
)
throw new MonthlyLimitError(
`Your workspace has reached its monthly spending limit of $${billing.monthlyLimit}. Manage your limits here: https://opencode.ai/workspace/${authInfo.workspaceID}/billing`,
t("zen.api.error.workspaceMonthlyLimitReached", {
amount: billing.monthlyLimit,
billingUrl,
}),
)
if (
@@ -708,7 +731,10 @@ export async function handler(
currentMonth === authInfo.user.timeMonthlyUsageUpdated.getUTCMonth()
)
throw new UserLimitError(
`You have reached your monthly spending limit of $${authInfo.user.monthlyLimit}. Manage your limits here: https://opencode.ai/workspace/${authInfo.workspaceID}/members`,
t("zen.api.error.userMonthlyLimitReached", {
amount: authInfo.user.monthlyLimit,
membersUrl,
}),
)
return "balance"
@@ -716,7 +742,7 @@ export async function handler(
function validateModelSettings(authInfo: AuthInfo) {
if (!authInfo) return
if (authInfo.isDisabled) throw new ModelError("Model is disabled")
if (authInfo.isDisabled) throw new ModelError(t("zen.api.error.modelDisabled"))
}
function updateProviderKey(authInfo: AuthInfo, providerInfo: ProviderInfo) {

View File

@@ -3,11 +3,14 @@ import { IpRateLimitTable } from "@opencode-ai/console-core/schema/ip.sql.js"
import { FreeUsageLimitError } from "./error"
import { logger } from "./logger"
import { ZenData } from "@opencode-ai/console-core/model.js"
import { i18n } from "~/i18n"
import { localeFromRequest } from "~/lib/language"
export function createRateLimiter(limit: ZenData.RateLimit | undefined, rawIp: string, headers: Headers) {
export function createRateLimiter(limit: ZenData.RateLimit | undefined, rawIp: string, request: Request) {
if (!limit) return
const dict = i18n(localeFromRequest(request))
const limitValue = limit.checkHeader && !headers.get(limit.checkHeader) ? limit.fallbackValue! : limit.value
const limitValue = limit.checkHeader && !request.headers.get(limit.checkHeader) ? limit.fallbackValue! : limit.value
const ip = !rawIp.length ? "unknown" : rawIp
const now = Date.now()
@@ -36,7 +39,7 @@ export function createRateLimiter(limit: ZenData.RateLimit | undefined, rawIp: s
logger.debug(`rate limit total: ${total}`)
if (total >= limitValue)
throw new FreeUsageLimitError(
`Rate limit exceeded. Please try again later.`,
dict["zen.api.error.rateLimitExceeded"],
limit.period === "day" ? getRetryAfterDay(now) : getRetryAfterHour(rows, intervals, limitValue, now),
)
},

View File

@@ -0,0 +1,157 @@
#!/usr/bin/env bun
import { Buffer } from "node:buffer"
import { $ } from "bun"
const { values } = parseArgs({
args: Bun.argv.slice(2),
options: {
"dry-run": { type: "boolean", default: false },
},
})
const dryRun = values["dry-run"]
import { parseArgs } from "node:util"
const repo = process.env.GH_REPO
if (!repo) throw new Error("GH_REPO is required")
const releaseId = process.env.OPENCODE_RELEASE
if (!releaseId) throw new Error("OPENCODE_RELEASE is required")
const token = process.env.GH_TOKEN ?? process.env.GITHUB_TOKEN
if (!token) throw new Error("GH_TOKEN or GITHUB_TOKEN is required")
const apiHeaders = {
Authorization: `token ${token}`,
Accept: "application/vnd.github+json",
}
const releaseRes = await fetch(`https://api.github.com/repos/${repo}/releases/${releaseId}`, {
headers: apiHeaders,
})
if (!releaseRes.ok) {
throw new Error(`Failed to fetch release: ${releaseRes.status} ${releaseRes.statusText}`)
}
type Asset = {
name: string
url: string
browser_download_url: string
}
type Release = {
tag_name?: string
assets?: Asset[]
}
const release = (await releaseRes.json()) as Release
const assets = release.assets ?? []
const assetByName = new Map(assets.map((asset) => [asset.name, asset]))
const latestAsset = assetByName.get("latest.json")
if (!latestAsset) throw new Error("latest.json asset not found")
const latestRes = await fetch(latestAsset.url, {
headers: {
Authorization: `token ${token}`,
Accept: "application/octet-stream",
},
})
if (!latestRes.ok) {
throw new Error(`Failed to fetch latest.json: ${latestRes.status} ${latestRes.statusText}`)
}
const latestText = new TextDecoder().decode(await latestRes.arrayBuffer())
const latest = JSON.parse(latestText)
const base = { ...latest }
delete base.platforms
const fetchSignature = async (asset: Asset) => {
const res = await fetch(asset.url, {
headers: {
Authorization: `token ${token}`,
Accept: "application/octet-stream",
},
})
if (!res.ok) {
throw new Error(`Failed to fetch signature: ${res.status} ${res.statusText}`)
}
return Buffer.from(await res.arrayBuffer()).toString()
}
const entries: Record<string, { url: string; signature: string }> = {}
const add = (key: string, asset: Asset, signature: string) => {
if (entries[key]) return
entries[key] = {
url: asset.browser_download_url,
signature,
}
}
const targets = [
{ key: "linux-x86_64-deb", asset: "opencode-desktop-linux-amd64.deb" },
{ key: "linux-x86_64-rpm", asset: "opencode-desktop-linux-x86_64.rpm" },
{ key: "linux-aarch64-deb", asset: "opencode-desktop-linux-arm64.deb" },
{ key: "linux-aarch64-rpm", asset: "opencode-desktop-linux-aarch64.rpm" },
{ key: "windows-x86_64-nsis", asset: "opencode-desktop-windows-x64.exe" },
{ key: "darwin-x86_64-app", asset: "opencode-desktop-darwin-x64.app.tar.gz" },
{
key: "darwin-aarch64-app",
asset: "opencode-desktop-darwin-aarch64.app.tar.gz",
},
]
for (const target of targets) {
const asset = assetByName.get(target.asset)
if (!asset) continue
const sig = assetByName.get(`${target.asset}.sig`)
if (!sig) continue
const signature = await fetchSignature(sig)
add(target.key, asset, signature)
}
const alias = (key: string, source: string) => {
if (entries[key]) return
const entry = entries[source]
if (!entry) return
entries[key] = entry
}
alias("linux-x86_64", "linux-x86_64-deb")
alias("linux-aarch64", "linux-aarch64-deb")
alias("windows-x86_64", "windows-x86_64-nsis")
alias("darwin-x86_64", "darwin-x86_64-app")
alias("darwin-aarch64", "darwin-aarch64-app")
const platforms = Object.fromEntries(
Object.keys(entries)
.sort()
.map((key) => [key, entries[key]]),
)
const output = {
...base,
platforms,
}
const dir = process.env.RUNNER_TEMP ?? "/tmp"
const file = `${dir}/latest.json`
await Bun.write(file, JSON.stringify(output, null, 2))
const tag = release.tag_name
if (!tag) throw new Error("Release tag not found")
if (dryRun) {
console.log(`dry-run: wrote latest.json for ${tag} to ${file}`)
process.exit(0)
}
await $`gh release upload ${tag} ${file} --clobber --repo ${repo}`
console.log(`finalized latest.json for ${tag}`)

View File

@@ -3,6 +3,32 @@ import { message } from "@tauri-apps/plugin-dialog"
import { initI18n, t } from "./i18n"
import { commands } from "./bindings"
function installError(error: unknown) {
const text = String(error)
if (text.includes("CLI installation is only supported on macOS & Linux")) {
return t("desktop.cli.error.unsupportedPlatform")
}
if (text.includes("Sidecar binary not found")) {
return t("desktop.cli.error.sidecarMissing")
}
if (text.includes("Failed to write install script")) {
return t("desktop.cli.error.scriptWriteFailed")
}
if (text.includes("Failed to set script permissions")) {
return t("desktop.cli.error.scriptPermissionFailed")
}
if (text.includes("Failed to run install script")) {
return t("desktop.cli.error.scriptRunFailed")
}
if (text.includes("Install script failed")) {
return t("desktop.cli.error.scriptFailed")
}
if (text.includes("Could not determine install path")) {
return t("desktop.cli.error.installPathUnknown")
}
return text || t("desktop.cli.error.unknown")
}
export async function installCli(): Promise<void> {
await initI18n()
@@ -10,6 +36,8 @@ export async function installCli(): Promise<void> {
const path = await commands.installCli()
await message(t("desktop.cli.installed.message", { path }), { title: t("desktop.cli.installed.title") })
} catch (e) {
await message(t("desktop.cli.failed.message", { error: String(e) }), { title: t("desktop.cli.failed.title") })
await message(t("desktop.cli.failed.message", { error: installError(e) }), {
title: t("desktop.cli.failed.title"),
})
}
}

View File

@@ -23,4 +23,37 @@ export const dict = {
"desktop.cli.installed.message": "تم تثبيت CLI في {{path}}\n\nأعد تشغيل الطرفية لاستخدام الأمر 'opencode'.",
"desktop.cli.failed.title": "فشل التثبيت",
"desktop.cli.failed.message": "فشل تثبيت CLI: {{error}}",
"desktop.menu.app": "OpenCode",
"desktop.menu.file": "ملف",
"desktop.menu.edit": "تعديل",
"desktop.menu.view": "عرض",
"desktop.menu.help": "مساعدة",
"desktop.menu.file.newSession": "جلسة جديدة",
"desktop.menu.file.openProject": "فتح مشروع...",
"desktop.menu.view.toggleSidebar": "تبديل الشريط الجانبي",
"desktop.menu.view.toggleTerminal": "تبديل الطرفية",
"desktop.menu.view.toggleFileTree": "تبديل شجرة الملفات",
"desktop.menu.view.back": "رجوع",
"desktop.menu.view.forward": "تقدم",
"desktop.menu.view.previousSession": "الجلسة السابقة",
"desktop.menu.view.nextSession": "الجلسة التالية",
"desktop.menu.help.documentation": "وثائق OpenCode",
"desktop.menu.help.supportForum": "منتدى الدعم",
"desktop.menu.help.shareFeedback": "مشاركة التعليقات",
"desktop.menu.help.reportBug": "الإبلاغ عن خطأ",
"desktop.cli.error.unsupportedPlatform": "تثبيت CLI مدعوم فقط على macOS و Linux.",
"desktop.cli.error.sidecarMissing": "ملف OpenCode CLI الثنائي مفقود. حاول إعادة تثبيت تطبيق سطح المكتب.",
"desktop.cli.error.scriptWriteFailed": "فشل تحضير برنامج تثبيت CLI.",
"desktop.cli.error.scriptPermissionFailed": "فشل جعل برنامج تثبيت CLI قابلاً للتنفيذ.",
"desktop.cli.error.scriptRunFailed": "فشل تشغيل برنامج تثبيت CLI.",
"desktop.cli.error.scriptFailed": "فشل برنامج تثبيت CLI.",
"desktop.cli.error.installPathUnknown": "تعذر تحديد مكان تثبيت CLI.",
"desktop.cli.error.unknown": "خطأ تثبيت غير معروف",
"desktop.loading.status.initial": "لحظة من فضلك...",
"desktop.loading.status.done": "تم الانتهاء",
"desktop.loading.status.migrating": "جارٍ ترحيل قاعدة البيانات الخاصة بك",
"desktop.loading.status.waiting": "قد يستغرق هذا بضع دقائق",
"desktop.loading.progressAria": "تقدم ترحيل قاعدة البيانات",
"desktop.server.local": "خادم محلي",
}

View File

@@ -24,4 +24,38 @@ export const dict = {
"desktop.cli.installed.message": "CLI instalada em {{path}}\n\nReinicie seu terminal para usar o comando 'opencode'.",
"desktop.cli.failed.title": "Falha na instalação",
"desktop.cli.failed.message": "Falha ao instalar a CLI: {{error}}",
"desktop.menu.app": "OpenCode",
"desktop.menu.file": "Arquivo",
"desktop.menu.edit": "Editar",
"desktop.menu.view": "Visualizar",
"desktop.menu.help": "Ajuda",
"desktop.menu.file.newSession": "Nova Sessão",
"desktop.menu.file.openProject": "Abrir Projeto...",
"desktop.menu.view.toggleSidebar": "Alternar Barra Lateral",
"desktop.menu.view.toggleTerminal": "Alternar Terminal",
"desktop.menu.view.toggleFileTree": "Alternar Árvore de Arquivos",
"desktop.menu.view.back": "Voltar",
"desktop.menu.view.forward": "Avançar",
"desktop.menu.view.previousSession": "Sessão Anterior",
"desktop.menu.view.nextSession": "Próxima Sessão",
"desktop.menu.help.documentation": "Documentação do OpenCode",
"desktop.menu.help.supportForum": "Fórum de Suporte",
"desktop.menu.help.shareFeedback": "Compartilhar Feedback",
"desktop.menu.help.reportBug": "Relatar um Bug",
"desktop.cli.error.unsupportedPlatform": "A instalação da CLI é suportada apenas no macOS e Linux.",
"desktop.cli.error.sidecarMissing":
"O binário da CLI do OpenCode está ausente. Tente reinstalar o aplicativo de desktop.",
"desktop.cli.error.scriptWriteFailed": "Falha ao preparar o script de instalação da CLI.",
"desktop.cli.error.scriptPermissionFailed": "Falha ao tornar o script de instalação da CLI executável.",
"desktop.cli.error.scriptRunFailed": "Falha ao executar o script de instalação da CLI.",
"desktop.cli.error.scriptFailed": "O instalador da CLI falhou.",
"desktop.cli.error.installPathUnknown": "Não foi possível determinar onde a CLI foi instalada.",
"desktop.cli.error.unknown": "Erro de instalação desconhecido",
"desktop.loading.status.initial": "Só um momento...",
"desktop.loading.status.done": "Tudo pronto",
"desktop.loading.status.migrating": "Migrando seu banco de dados",
"desktop.loading.status.waiting": "Isso pode levar alguns minutos",
"desktop.loading.progressAria": "Progresso da migração do banco de dados",
"desktop.server.local": "Servidor Local",
}

View File

@@ -25,4 +25,38 @@ export const dict = {
"CLI je instaliran u {{path}}\n\nRestartuj terminal da bi koristio komandu 'opencode'.",
"desktop.cli.failed.title": "Instalacija nije uspjela",
"desktop.cli.failed.message": "Neuspjela instalacija CLI-a: {{error}}",
"desktop.menu.app": "OpenCode",
"desktop.menu.file": "Datoteka",
"desktop.menu.edit": "Uredi",
"desktop.menu.view": "Prikaz",
"desktop.menu.help": "Pomoć",
"desktop.menu.file.newSession": "Nova sesija",
"desktop.menu.file.openProject": "Otvori projekat...",
"desktop.menu.view.toggleSidebar": "Prebaci bočnu traku",
"desktop.menu.view.toggleTerminal": "Prebaci terminal",
"desktop.menu.view.toggleFileTree": "Prebaci stablo datoteka",
"desktop.menu.view.back": "Nazad",
"desktop.menu.view.forward": "Naprijed",
"desktop.menu.view.previousSession": "Prethodna sesija",
"desktop.menu.view.nextSession": "Sljedeća sesija",
"desktop.menu.help.documentation": "OpenCode Dokumentacija",
"desktop.menu.help.supportForum": "Forum za podršku",
"desktop.menu.help.shareFeedback": "Podijeli povratne informacije",
"desktop.menu.help.reportBug": "Prijavi grešku",
"desktop.cli.error.unsupportedPlatform": "Instalacija CLI-a je podržana samo na macOS-u i Linux-u.",
"desktop.cli.error.sidecarMissing":
"Nedostaje binarna datoteka OpenCode CLI-a. Pokušaj ponovo instalirati desktop aplikaciju.",
"desktop.cli.error.scriptWriteFailed": "Nije uspjela priprema skripte za instalaciju CLI-a.",
"desktop.cli.error.scriptPermissionFailed": "Nije uspjelo postavljanje izvršnih dozvola za instalaciju CLI-a.",
"desktop.cli.error.scriptRunFailed": "Nije uspjelo pokretanje skripte za instalaciju CLI-a.",
"desktop.cli.error.scriptFailed": "Instalacija CLI-a nije uspjela.",
"desktop.cli.error.installPathUnknown": "Nije bilo moguće utvrditi gdje je instaliran CLI.",
"desktop.cli.error.unknown": "Nepoznata greška pri instalaciji",
"desktop.loading.status.initial": "Samo trenutak...",
"desktop.loading.status.done": "Sve je gotovo",
"desktop.loading.status.migrating": "Migracija baze podataka u toku",
"desktop.loading.status.waiting": "Ovo može potrajati nekoliko minuta",
"desktop.loading.progressAria": "Napredak migracije baze podataka",
"desktop.server.local": "Lokalni server",
}

View File

@@ -25,4 +25,37 @@ export const dict = {
"CLI installeret i {{path}}\n\nGenstart din terminal for at bruge 'opencode'-kommandoen.",
"desktop.cli.failed.title": "Installation mislykkedes",
"desktop.cli.failed.message": "Kunne ikke installere CLI: {{error}}",
"desktop.menu.app": "OpenCode",
"desktop.menu.file": "Filer",
"desktop.menu.edit": "Rediger",
"desktop.menu.view": "Vis",
"desktop.menu.help": "Hjælp",
"desktop.menu.file.newSession": "Ny session",
"desktop.menu.file.openProject": "Åbn projekt...",
"desktop.menu.view.toggleSidebar": "Slå sidepanel til/fra",
"desktop.menu.view.toggleTerminal": "Slå terminal til/fra",
"desktop.menu.view.toggleFileTree": "Slå filoversigt til/fra",
"desktop.menu.view.back": "Tilbage",
"desktop.menu.view.forward": "Fremad",
"desktop.menu.view.previousSession": "Forrige session",
"desktop.menu.view.nextSession": "Næste session",
"desktop.menu.help.documentation": "OpenCode Dokumentation",
"desktop.menu.help.supportForum": "Supportforum",
"desktop.menu.help.shareFeedback": "Del feedback",
"desktop.menu.help.reportBug": "Rapporter en fejl",
"desktop.cli.error.unsupportedPlatform": "CLI-installation understøttes kun på macOS og Linux.",
"desktop.cli.error.sidecarMissing": "OpenCode CLI-binærfil mangler. Prøv at geninstallere desktop-appen.",
"desktop.cli.error.scriptWriteFailed": "Kunne ikke forberede CLI-installationsscriptet.",
"desktop.cli.error.scriptPermissionFailed": "Kunne ikke gøre CLI-installationsscriptet eksekverbart.",
"desktop.cli.error.scriptRunFailed": "Kunne ikke køre CLI-installationsscriptet.",
"desktop.cli.error.scriptFailed": "CLI-installationsprogrammet mislykkedes.",
"desktop.cli.error.installPathUnknown": "Kunne ikke fastslå, hvor CLI'en blev installeret.",
"desktop.cli.error.unknown": "Ukendt installationsfejl",
"desktop.loading.status.initial": "Lige et øjeblik...",
"desktop.loading.status.done": "Helt færdig",
"desktop.loading.status.migrating": "Migrerer din database",
"desktop.loading.status.waiting": "Dette kan tage et par minutter",
"desktop.loading.progressAria": "Status for databasemigrering",
"desktop.server.local": "Lokal server",
}

View File

@@ -25,4 +25,38 @@ export const dict = {
"CLI wurde in {{path}} installiert\n\nStarten Sie Ihr Terminal neu, um den Befehl 'opencode' zu verwenden.",
"desktop.cli.failed.title": "Installation fehlgeschlagen",
"desktop.cli.failed.message": "CLI konnte nicht installiert werden: {{error}}",
"desktop.menu.app": "OpenCode",
"desktop.menu.file": "Datei",
"desktop.menu.edit": "Bearbeiten",
"desktop.menu.view": "Ansicht",
"desktop.menu.help": "Hilfe",
"desktop.menu.file.newSession": "Neue Sitzung",
"desktop.menu.file.openProject": "Projekt öffnen...",
"desktop.menu.view.toggleSidebar": "Seitenleiste umschalten",
"desktop.menu.view.toggleTerminal": "Terminal umschalten",
"desktop.menu.view.toggleFileTree": "Dateibaum umschalten",
"desktop.menu.view.back": "Zurück",
"desktop.menu.view.forward": "Vorwärts",
"desktop.menu.view.previousSession": "Vorherige Sitzung",
"desktop.menu.view.nextSession": "Nächste Sitzung",
"desktop.menu.help.documentation": "OpenCode-Dokumentation",
"desktop.menu.help.supportForum": "Support-Forum",
"desktop.menu.help.shareFeedback": "Feedback teilen",
"desktop.menu.help.reportBug": "Einen Fehler melden",
"desktop.cli.error.unsupportedPlatform": "Die CLI-Installation wird nur unter macOS und Linux unterstützt.",
"desktop.cli.error.sidecarMissing":
"Das OpenCode CLI-Binary fehlt. Versuchen Sie, die Desktop-App neu zu installieren.",
"desktop.cli.error.scriptWriteFailed": "Das CLI-Installationsskript konnte nicht vorbereitet werden.",
"desktop.cli.error.scriptPermissionFailed": "Das CLI-Installationsskript konnte nicht ausführbar gemacht werden.",
"desktop.cli.error.scriptRunFailed": "Das CLI-Installationsskript konnte nicht ausgeführt werden.",
"desktop.cli.error.scriptFailed": "Das CLI-Installationsprogramm ist fehlgeschlagen.",
"desktop.cli.error.installPathUnknown": "Es konnte nicht ermittelt werden, wo die CLI installiert wurde.",
"desktop.cli.error.unknown": "Unbekannter Installationsfehler",
"desktop.loading.status.initial": "Einen Moment bitte...",
"desktop.loading.status.done": "Alles erledigt",
"desktop.loading.status.migrating": "Ihre Datenbank wird migriert",
"desktop.loading.status.waiting": "Dies kann einige Minuten dauern",
"desktop.loading.progressAria": "Fortschritt der Datenbankmigration",
"desktop.server.local": "Lokaler Server",
}

View File

@@ -3,6 +3,24 @@ export const dict = {
"desktop.menu.installCli": "Install CLI...",
"desktop.menu.reloadWebview": "Reload Webview",
"desktop.menu.restart": "Restart",
"desktop.menu.app": "OpenCode",
"desktop.menu.file": "File",
"desktop.menu.edit": "Edit",
"desktop.menu.view": "View",
"desktop.menu.help": "Help",
"desktop.menu.file.newSession": "New Session",
"desktop.menu.file.openProject": "Open Project...",
"desktop.menu.view.toggleSidebar": "Toggle Sidebar",
"desktop.menu.view.toggleTerminal": "Toggle Terminal",
"desktop.menu.view.toggleFileTree": "Toggle File Tree",
"desktop.menu.view.back": "Back",
"desktop.menu.view.forward": "Forward",
"desktop.menu.view.previousSession": "Previous Session",
"desktop.menu.view.nextSession": "Next Session",
"desktop.menu.help.documentation": "OpenCode Documentation",
"desktop.menu.help.supportForum": "Support Forum",
"desktop.menu.help.shareFeedback": "Share Feedback",
"desktop.menu.help.reportBug": "Report a Bug",
"desktop.dialog.chooseFolder": "Choose a folder",
"desktop.dialog.chooseFile": "Choose a file",
@@ -24,4 +42,20 @@ export const dict = {
"desktop.cli.installed.message": "CLI installed to {{path}}\n\nRestart your terminal to use the 'opencode' command.",
"desktop.cli.failed.title": "Installation Failed",
"desktop.cli.failed.message": "Failed to install CLI: {{error}}",
"desktop.cli.error.unsupportedPlatform": "CLI installation is only supported on macOS and Linux.",
"desktop.cli.error.sidecarMissing": "OpenCode CLI binary is missing. Try reinstalling the desktop app.",
"desktop.cli.error.scriptWriteFailed": "Failed to prepare CLI installer script.",
"desktop.cli.error.scriptPermissionFailed": "Failed to make CLI installer executable.",
"desktop.cli.error.scriptRunFailed": "Failed to run CLI installer script.",
"desktop.cli.error.scriptFailed": "CLI installer failed.",
"desktop.cli.error.installPathUnknown": "Could not determine where the CLI was installed.",
"desktop.cli.error.unknown": "Unknown installation error",
"desktop.loading.status.initial": "Just a moment...",
"desktop.loading.status.done": "All done",
"desktop.loading.status.migrating": "Migrating your database",
"desktop.loading.status.waiting": "This may take a couple of minutes",
"desktop.loading.progressAria": "Database migration progress",
"desktop.server.local": "Local Server",
}

Some files were not shown because too many files have changed in this diff Show More