diff options
| author | Adam <[email protected]> | 2026-02-27 15:50:50 -0600 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-02-27 15:50:50 -0600 |
| commit | 7f851da15ecde7db6535aae1f13009d3d6068daf (patch) | |
| tree | e8978e858c09a6507dfe0ad40b32b008d634b82e /packages/console/app/src/routes/zen/util | |
| parent | a3bdb974b36714ace5e2a41e76da8aa6682eb4c8 (diff) | |
| download | opencode-7f851da15ecde7db6535aae1f13009d3d6068daf.tar.gz opencode-7f851da15ecde7db6535aae1f13009d3d6068daf.zip | |
chore(console): i18n sync (#15360)
Diffstat (limited to 'packages/console/app/src/routes/zen/util')
| -rw-r--r-- | packages/console/app/src/routes/zen/util/handler.ts | 74 | ||||
| -rw-r--r-- | packages/console/app/src/routes/zen/util/rateLimiter.ts | 9 |
2 files changed, 56 insertions, 27 deletions
diff --git a/packages/console/app/src/routes/zen/util/handler.ts b/packages/console/app/src/routes/zen/util/handler.ts index 3f6d69bf2..d3a25c5f6 100644 --- a/packages/console/app/src/routes/zen/util/handler.ts +++ b/packages/console/app/src/routes/zen/util/handler.ts @@ -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. You can continue using free models.`, + 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. You can continue using free models.`, + 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. You can continue using free models.`, + 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) { diff --git a/packages/console/app/src/routes/zen/util/rateLimiter.ts b/packages/console/app/src/routes/zen/util/rateLimiter.ts index 5e4f31e67..6325a7b4d 100644 --- a/packages/console/app/src/routes/zen/util/rateLimiter.ts +++ b/packages/console/app/src/routes/zen/util/rateLimiter.ts @@ -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), ) }, |
