From 5606cef2243d99b72af8461cdfc62fa792a38fa6 Mon Sep 17 00:00:00 2001 From: danielgap Date: Thu, 29 Oct 2026 23:29:18 +0100 Subject: [PATCH 1/3] =?UTF-8?q?feat(i18n):=20migrar=20CliConnect=20y=20Acc?= =?UTF-8?q?ountEditor=20a=20los=20cat=C3=A1logos=20ES/EN=20=E2=80=94=20T2c?= =?UTF-8?q?=20slice=202=20C1a=20(#66)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CliConnect (vía CliConnectView, extraída verbatim para SSR) y AccountEditor pasan sus textos a settings.providers.{cliConnect,accountEditor}.* con 4 claves compartidas; con es todo se ve igual. CLI_INSTALL gana whereKey para que CliMissing traduzca por clave y no comparando 'una terminal' (nota 1 de #204). Marcas, rutas, comandos, planes con cifra y rótulos de UIs de terceros quedan literales. --- src/Settings-accounts-i18n.test.tsx | 476 ++++++++++++++++++++++++++++ src/Settings.tsx | 205 ++++++++---- src/i18n/en.ts | 62 ++++ src/i18n/es.ts | 69 ++++ src/lib/accounts.ts | 13 +- 5 files changed, 755 insertions(+), 70 deletions(-) create mode 100644 src/Settings-accounts-i18n.test.tsx diff --git a/src/Settings-accounts-i18n.test.tsx b/src/Settings-accounts-i18n.test.tsx new file mode 100644 index 0000000..f3116d5 --- /dev/null +++ b/src/Settings-accounts-i18n.test.tsx @@ -0,0 +1,476 @@ +// Corte visible de i18n para los pasos de conexión por CLI (`CliConnect`) y el +// editor de cuentas adicionales (`AccountEditor`) de Ajustes › Proveedores (#66, +// T2c slice 2 C1a), renderizados en servidor (renderToStaticMarkup, sin DOM +// falso) con los literales exactos: en español tal y como estaban en el código y +// en inglés tras la migración. También cubre `whereKey` de `CLI_INSTALL`: el +// aviso de `CliMissing` se traduce por clave, no comparando un texto español. +import { renderToStaticMarkup } from 'react-dom/server'; +import { describe, expect, it } from 'vitest'; +import { CLI_INSTALL, defaultSessionDir } from './lib/accounts'; +import type { CodexInstallController } from './lib/codex-install'; +import type { Account, AccountProvider, Locale } from './lib/types'; +import { AccountEditor, CliConnectView, CliMissing } from './Settings'; + +const noop = () => {}; + +const expectAll = (html: string, texts: string[], label: string) => { + for (const text of texts) expect(html, `falta «${text}» en ${label}`).toContain(text); +}; +const expectNone = (html: string, texts: string[], label: string) => { + for (const text of texts) expect(html, `sobra «${text}» en ${label}`).not.toContain(text); +}; + +const health = (locale: Locale, over: { dirty?: boolean } = {}) => ({ + saved: null, + secretsSig: '', + dirty: over.dirty ?? false, + hidden: false, + saving: false, + onSave: () => Promise.resolve(true), + locale, +}); + +// `CodexInstall` solo se pinta con `codex` y el CLI ausente; aquí no se usa. +const codexInstall = {} as CodexInstallController; + +const connect = ( + locale: Locale, + provider: 'claude' | 'gemini', + over: { cliMissing?: boolean; persisted?: boolean; extra?: boolean; dirty?: boolean; connected?: boolean; loginError?: string | null } = {}, +) => + renderToStaticMarkup( + , + ); + +describe('CliConnect steps (SSR, i18n)', () => { + const states = (locale: Locale) => ({ + missingClaude: connect(locale, 'claude', { cliMissing: true }), + claudeDirty: connect(locale, 'claude', { persisted: true, dirty: true }), + claudeSaved: connect(locale, 'claude', { persisted: true }), + geminiExtra: connect(locale, 'gemini', { extra: true, persisted: true }), + geminiConnected: connect(locale, 'gemini', { persisted: true, connected: true, extra: true }), + failed: connect(locale, 'claude', { persisted: true, loginError: 'boom' }), + }); + + it('renders the Spanish literals copied verbatim from the current code', () => { + const s = states('es'); + expectAll( + s.missingClaude, + [ + 'Instala Claude Code', + 'Hace falta para iniciar sesión y para renovarla cuando caduque, aunque ahora funcione.', + 'Falta Claude Code en este equipo.', + 'Inicia sesión', + 'Se abre una terminal con claude (si no te lo pide, escribe /login). El navegador hace el login oficial y el CLI guarda la sesión, que Quorb solo lee.', + 'Guardar e iniciar sesión', + 'Comprobación', + 'Se hace sola en cuanto guardes e inicies sesión.', + ], + 'es', + ); + expect(s.claudeDirty).toContain('Guardar e iniciar sesión'); + expectAll(s.claudeSaved, ['>Iniciar sesión', 'Comprobando la conexión…'], 'es'); + expect(s.claudeSaved).not.toContain('Guardar e iniciar sesión'); + expectAll( + s.geminiExtra, + [ + 'Inicia sesión con la otra cuenta', + 'Se abre una terminal con gemini (elige «Login with Google»). El navegador hace el login oficial y el CLI guarda la sesión, que Quorb solo lee.', + ' Entra con la cuenta que quieres añadir, no con la principal: si el navegador ya tiene abierta la principal, usa una ventana privada.', + ], + 'es', + ); + expectAll( + s.geminiConnected, + [ + 'Fichero de sesión encontrado', + 'que Quorb solo lee. El fichero existe, pero no confirma que la sesión siga siendo válida: míralo en la comprobación de abajo.', + '>Volver a abrir el login', + ], + 'es', + ); + expectAll(s.failed, ['boom'], 'es'); + expectNone(s.claudeSaved, ['Fichero de sesión encontrado', 'Entra con la cuenta'], 'es'); + }); + + it('renders the English texts when the locale is en, with no Spanish left', () => { + const s = states('en'); + expectAll( + s.missingClaude, + [ + 'Install Claude Code', + 'It is needed to sign in and to renew the session when it expires, even if it works now.', + 'Claude Code is missing on this computer.', + 'Sign in', + 'A terminal opens with claude (if it does not ask, type /login). The browser does the official login and the CLI stores the session, which Quorb only reads.', + 'Save and sign in', + 'Check', + 'It runs on its own as soon as you save and sign in.', + ], + 'en', + ); + expect(s.claudeDirty).toContain('Save and sign in'); + expectAll(s.claudeSaved, ['>Sign in', 'Checking the connection…'], 'en'); + expectAll( + s.geminiExtra, + [ + 'Sign in with the other account', + 'A terminal opens with gemini (choose “Login with Google”).', + ' Sign in with the account you want to add, not the main one: if the browser already has the main one open, use a private window.', + ], + 'en', + ); + expectAll( + s.geminiConnected, + [ + 'Session file found', + 'which Quorb only reads. The file exists, but it does not confirm the session is still valid: check the verification below.', + '>Open the login again', + ], + 'en', + ); + expectNone( + Object.values(s).join('\n'), + [ + 'Instala', + 'Hace falta', + 'Inicia sesión', + 'Iniciar sesión', + 'Se abre una terminal', + 'si no te lo pide', + 'elige', + 'Fichero', + 'El fichero existe', + 'Entra con la cuenta', + 'Volver a abrir', + 'Guardar e iniciar', + 'Comprobación', + 'Se hace sola', + 'su CLI', + ], + 'en', + ); + }); +}); + +describe('AccountEditor (SSR, i18n)', () => { + const account = (provider: AccountProvider, over: Partial = {}): Account => + ({ + id: `${provider}:b`, + provider, + label: 'Mi cuenta', + enabled: true, + configDir: null, + username: 'egdev6', + plan: '', + customAllowance: null, + monthlyBudgetUsd: null, + ...over, + }) as Account; + + const editor = (locale: Locale, a: Account, persisted = false) => + renderToStaticMarkup( + , + ); + + const claude = account('claude'); + const dir = defaultSessionDir(claude, null); + + it('renders the Spanish literals copied verbatim from the current code', () => { + const base = editor('es', claude); + expectAll( + base, + [ + '>Cuenta adicional

', + '

Mi cuenta

', + 'Nombre', + 'Se ve en la tira junto al proveedor.', + 'Mostrar en el widget', + 'aria-label="Cómo conectar esta cuenta"', + 'Ponle nombre', + 'Arriba, para distinguirla de la principal en la tira (por ejemplo, «Trabajo»).', + 'Comprobación', + 'Avanzado · carpeta de sesión', + 'Eliminar esta cuenta', + 'Al guardar se borran también sus claves del almacén seguro del sistema.', + ], + 'es', + ); + expectAll( + base, + [ + 'Cada cuenta guarda su sesión en su propia carpeta, así no toca la principal: el login se abre con CLAUDE_CONFIG_DIR apuntando a ella. Si la comprobación pide renovar la sesión, «Volver a abrir el login» abre de nuevo el CLI.', + 'Carpeta de sesión', + `Vacío = ${dir}. Puedes usar una que ya tengas. Quorb lee .credentials.json dentro de ella.`, + ], + 'es', + ); + // Cuenta sin guardar: el paso de comprobación pide guardar; guardada, comprueba. + expectAll(editor('es', account('openrouter')), ['Guarda la cuenta para comprobar que funciona.', 'Guardar y comprobar'], 'es'); + expect(editor('es', account('openrouter'), true)).not.toContain('Guarda la cuenta para comprobar'); + + expectAll( + editor('es', account('cursor')), + [ + 'Pega el token de esta cuenta', + 'La app de Cursor guarda una sola sesión (la principal). Entra en cursor.com con esta cuenta y copia la cookie WorkosCursorSessionToken.', + 'Token de la cuenta', + ], + 'es', + ); + const copilot = editor('es', account('copilot', { plan: 'custom' })); + expectAll( + copilot, + [ + 'Usuario y plan de GitHub', + 'Usuario de GitHub', + 'Plan', + '>Free · 50<', + '>Pro · 300<', + '>Pro+ · 1500<', + '>Business · 300<', + '>Enterprise · 1000<', + '>Otra cantidad<', + 'Peticiones premium incluidas al mes', + 'Token de solo lectura', + 'Token de GitHub de esta cuenta (fine-grained)', + 'Créalo con esta cuenta en GitHub › Fine-grained tokens con el permiso «Plan» (solo lectura).', + ], + 'es', + ); + expectAll( + editor('es', account('nan')), + [ + 'Conecta la cuenta o pega una API key', + 'API key (alternativa)', + 'Plan', + 'La URL base y el resto de ajustes avanzados son los de la cuenta principal.', + '>nan_community<', + '>nan_member<', + '>GLM Premium<', + ], + 'es', + ); + expectAll( + editor('es', account('openrouter')), + ['API key', 'Puede ser de otra cuenta o de la misma con otro límite. openrouter.ai › Keys'], + 'es', + ); + expectAll(editor('es', account('zai')), ['API key', 'Créala en z.ai › API Keys.'], 'es'); + expectAll( + editor('es', account('opencode_go')), + ['API key de OpenCode Go', 'Créala en OpenCode › Console.'], + 'es', + ); + expectAll( + editor('es', account('anthropic_api')), + [ + 'Admin key de esta organización', + 'Admin key', + 'Cada organización tiene su propia Admin key.', + 'Presupuesto mensual (opcional)', + 'USD al mes', + ], + 'es', + ); + }); + + it('renders the English texts when the locale is en, with no Spanish left', () => { + const base = editor('en', claude); + expectAll( + base, + [ + '>Additional account

', + '

Mi cuenta

', + 'Name', + 'Shown on the strip next to the provider.', + 'Show in the widget', + 'aria-label="How to connect this account"', + 'Name it', + 'Above, to tell it apart from the main one on the strip (for example, “Work”).', + 'Check', + 'Advanced · session folder', + 'Remove this account', + 'Saving also deletes its keys from the system secure store.', + 'Each account keeps its session in its own folder, so it does not touch the main one: the login opens with CLAUDE_CONFIG_DIR pointing to it. If the check asks you to renew the session, “Open the login again” opens the CLI again.', + 'Session folder', + `Empty = ${dir}. You can use one you already have. Quorb reads .credentials.json inside it.`, + ], + 'en', + ); + const openrouter = editor('en', account('openrouter')); + expectAll(openrouter, ['Save the account to check that it works.', 'Save and check'], 'en'); + + const cursor = editor('en', account('cursor')); + expectAll( + cursor, + [ + 'Paste this account token', + 'The Cursor app stores a single session (the main one). Sign in at cursor.com with this account and copy the WorkosCursorSessionToken cookie.', + 'Account token', + ], + 'en', + ); + const copilot = editor('en', account('copilot', { plan: 'custom' })); + expectAll( + copilot, + [ + 'GitHub user and plan', + 'GitHub username', + 'Plan', + '>Free · 50<', + '>Pro · 300<', + '>Pro+ · 1500<', + '>Business · 300<', + '>Enterprise · 1000<', + '>Other amount<', + 'Premium requests included per month', + 'Read-only token', + 'GitHub token for this account (fine-grained)', + 'Create it with this account at GitHub › Fine-grained tokens with the “Plan” permission (read-only).', + ], + 'en', + ); + const nan = editor('en', account('nan')); + expectAll( + nan, + [ + 'Connect the account or paste an API key', + 'API key (alternative)', + 'Plan', + 'The base URL and the other advanced settings are those of the main account.', + '>nan_community<', + '>nan_member<', + '>GLM Premium<', + ], + 'en', + ); + const zai = editor('en', account('zai')); + expectAll(openrouter, ['API key', 'It can be from another account or from the same one with a different limit. openrouter.ai › Keys'], 'en'); + expectAll(zai, ['API key', 'Create it at z.ai › API Keys.'], 'en'); + const opencode = editor('en', account('opencode_go')); + expectAll(opencode, ['OpenCode Go API key', 'Create it at OpenCode › Console.'], 'en'); + const admin = editor('en', account('anthropic_api')); + expectAll( + admin, + [ + 'Admin key for this organization', + 'Each organization has its own Admin key.', + 'Monthly budget (optional)', + 'USD per month', + ], + 'en', + ); + // NanConnect y el resto de paneles siguen en español hasta C2 (fuera de este + // corte): solo se comprueba lo que pinta `AccountEditor` en sí. + expectNone( + [base, openrouter, cursor, copilot, nan, zai, opencode, admin].join('\n'), + [ + 'Cuenta adicional', + 'Nombre', + 'Se ve en la tira', + 'Mostrar en el widget', + 'Cómo conectar', + 'Ponle nombre', + 'Trabajo', + 'Comprobación', + 'Guarda la cuenta', + 'Guardar y comprobar', + 'Avanzado', + 'Cada cuenta guarda', + 'Carpeta de sesión', + 'Vacío', + 'Puedes usar', + 'dentro de ella', + 'Eliminar esta cuenta', + 'Al guardar se borran', + 'Pega el token', + 'La app de Cursor', + 'Token de', + 'Usuario', + 'Otra cantidad', + 'Peticiones premium', + 'solo lectura', + 'Créalo', + 'Créala', + 'Conecta la cuenta', + '(alternativa)', + 'La URL base', + 'Puede ser de otra cuenta', + 'API key de OpenCode', + 'Admin key de esta', + 'Cada organización', + 'Presupuesto mensual', + 'USD al mes', + ], + 'en', + ); + }); +}); + +describe('CLI_INSTALL.whereKey (CliMissing, i18n)', () => { + const missing = (locale: Locale, provider: 'claude' | 'codex' | 'gemini') => renderToStaticMarkup(); + + it('translates the terminal phrase through whereKey for codex and gemini', () => { + for (const provider of ['codex', 'gemini'] as const) { + expect(CLI_INSTALL[provider]?.whereKey, provider).toBe('settings.providers.cliMissing.terminal'); + expectAll(missing('es', provider), ['Instálalo desde una terminal (necesita Node.js);'], `es ${provider}`); + expectAll(missing('en', provider), ['Install it from a terminal (needs Node.js);'], `en ${provider}`); + expectNone(missing('en', provider), ['una terminal', 'Instálalo'], `en ${provider}`); + } + }); + + it('keeps PowerShell literal for claude, with no whereKey', () => { + expect(CLI_INSTALL.claude?.whereKey).toBeUndefined(); + expect(CLI_INSTALL.claude?.where).toBe('PowerShell'); + expectAll(missing('es', 'claude'), ['Instálalo desde PowerShell; Quorb'], 'es'); + expectAll(missing('en', 'claude'), ['Install it from PowerShell; Quorb'], 'en'); + }); + + it('does not depend on the Spanish text of the install data', () => { + const gemini = CLI_INSTALL.gemini as { where?: string }; + const before = gemini.where; + gemini.where = 'otra cosa'; + try { + expectAll(missing('en', 'gemini'), ['Install it from a terminal (needs Node.js);'], 'en'); + expectNone(missing('en', 'gemini'), ['otra cosa'], 'en'); + } finally { + if (before === undefined) delete gemini.where; + else gemini.where = before; + } + }); +}); diff --git a/src/Settings.tsx b/src/Settings.tsx index ca15629..0152702 100644 --- a/src/Settings.tsx +++ b/src/Settings.tsx @@ -576,7 +576,7 @@ export function CliMissing({ provider, locale = 'es' }: { provider: AccountProvi {t(locale, 'settings.providers.cliMissing.title', { name: how.name })}{' '} {t(locale, 'settings.providers.cliMissing.body', { - where: how.where === 'una terminal' ? t(locale, 'settings.providers.cliMissing.terminal') : how.where, + where: how.whereKey ? t(locale, how.whereKey) : how.where, needs: how.needs ? t(locale, 'settings.providers.cliMissing.needs', { needs: how.needs }) : '', })} @@ -784,6 +784,7 @@ function useCliSession(id: string, active: boolean): boolean { * adicional: instalar el CLI si falta, iniciar sesión y comprobar que funciona. */ function CliConnect({ + locale, id, provider, loginCommand, @@ -794,6 +795,8 @@ function CliConnect({ codexInstall, onCodexRecheck, }: { + /** Idioma de los pasos. */ + locale: Locale; id: string; provider: AccountProvider; loginCommand: string; @@ -829,28 +832,88 @@ function CliConnect({ setBusy(false); } }; + return ( + + ); +} + +/** Vista de los pasos de conexión por CLI a partir de su estado. Extraída para + * poder renderizar en pruebas cada estado con el locale explícito. */ +export function CliConnectView({ + locale, + id, + provider, + loginCommand, + cliMissing, + persisted, + extra, + health, + codexInstall, + onCodexRecheck, + connected, + busy, + loginError, + onConnect, +}: { + locale: Locale; + id: string; + provider: AccountProvider; + loginCommand: string; + cliMissing: boolean; + persisted: boolean; + extra: boolean; + health: HealthProps; + codexInstall: CodexInstallController; + onCodexRecheck: () => void; + /** Hay fichero de sesión del CLI. */ + connected: boolean; + /** Hay un login o guardado en curso. */ + busy: boolean; + /** Diagnóstico del último intento de login que falló. */ + loginError: string | null; + onConnect: () => void; +}) { return ( <> {cliMissing && (
  • - Instala {CLI_INSTALL[provider]?.name ?? 'su CLI'} - Hace falta para iniciar sesión y para renovarla cuando caduque, aunque ahora funcione. - {provider === 'codex' ? : } + {t(locale, 'settings.providers.cliConnect.installTitle', { name: CLI_INSTALL[provider]?.name ?? t(locale, 'settings.providers.cliConnect.cliFallback') })} + {t(locale, 'settings.providers.cliConnect.installBody')} + {provider === 'codex' ? : }
  • )}
  • - {connected ? 'Fichero de sesión encontrado' : extra ? 'Inicia sesión con la otra cuenta' : 'Inicia sesión'} + {connected ? t(locale, 'settings.providers.cliConnect.sessionFound') : extra ? t(locale, 'settings.providers.cliConnect.loginOther') : t(locale, 'settings.providers.cliConnect.login')} - Se abre una terminal con {loginCommand} - {provider === 'claude' && <> (si no te lo pide, escribe /login)} - {provider === 'gemini' && <> (elige «Login with Google»)}. El navegador hace el login oficial y el CLI guarda la sesión, que - Quorb solo lee. - {connected && ' El fichero existe, pero no confirma que la sesión siga siendo válida: míralo en la comprobación de abajo.'} - {extra && ' Entra con la cuenta que quieres añadir, no con la principal: si el navegador ya tiene abierta la principal, usa una ventana privada.'} + {t(locale, 'settings.providers.cliConnect.opensTerminal')}{' '} + {loginCommand} + {provider === 'claude' && <>{t(locale, 'settings.providers.cliConnect.claudeLoginHint')} /login)} + {provider === 'gemini' && <>{t(locale, 'settings.providers.cliConnect.geminiLoginHint')}}{t(locale, 'settings.providers.cliConnect.browserLogin')} + {connected && t(locale, 'settings.providers.cliConnect.fileExists')} + {extra && t(locale, 'settings.providers.cliConnect.extraNote')} - {loginError && ( @@ -860,11 +923,11 @@ function CliConnect({ )}
  • {persisted ? ( - + ) : (
  • - Comprobación - Se hace sola en cuanto guardes e inicies sesión. + {t(locale, 'settings.providers.checkTitle')} + {t(locale, 'settings.providers.cliConnect.checkAuto')}
  • )} @@ -872,7 +935,8 @@ function CliConnect({ } /** Configuración de una cuenta adicional: nombre, si se muestra y cómo se conecta, en pasos. */ -function AccountEditor({ +export function AccountEditor({ + locale, account: a, visible, onVisibility, @@ -887,6 +951,8 @@ function AccountEditor({ codexInstall, onCodexRecheck, }: { + /** Idioma del editor. */ + locale: Locale; account: Account; visible: boolean; onVisibility: (enabled: boolean) => void; @@ -914,40 +980,40 @@ function AccountEditor({ ) : (
  • - Comprobación - Guarda la cuenta para comprobar que funciona. + {t(locale, 'settings.providers.checkTitle')} + {t(locale, 'settings.providers.accountEditor.checkSave')}
  • ); return ( -
    +
    - + onChange({ label: e.target.value })} />
    - + -
      +
      1. - Ponle nombre - Arriba, para distinguirla de la principal en la tira (por ejemplo, «Trabajo»). + {t(locale, 'settings.providers.accountEditor.nameStep')} + {t(locale, 'settings.providers.accountEditor.nameStepBody')}
      2. {session && ( - + )} {a.provider === 'cursor' && ( <>
      3. - Pega el token de esta cuenta - La app de Cursor guarda una sola sesión (la principal). Entra en cursor.com con esta cuenta y copia la cookie WorkosCursorSessionToken. - + {t(locale, 'settings.providers.accountEditor.cursorTokenTitle')} + {t(locale, 'settings.providers.accountEditor.cursorTokenBody')} +
      4. {check} @@ -956,37 +1022,37 @@ function AccountEditor({ {a.provider === 'copilot' && ( <>
      5. - Usuario y plan de GitHub + {t(locale, 'settings.providers.accountEditor.copilotUserPlan')}
        - + onChange({ username: e.target.value })} /> - +
        {a.plan === 'custom' && ( - + onChange({ customAllowance: numOrNull(e.target.value) })} /> )}
      6. - Token de solo lectura + {t(locale, 'settings.providers.accountEditor.readOnlyToken')} Créalo con esta cuenta en GitHub › Fine-grained tokens con el permiso «Plan» (solo lectura).} + hint={<>{t(locale, 'settings.providers.accountEditor.githubTokenCreateIn')} GitHub › Fine-grained tokens {t(locale, 'settings.providers.accountEditor.githubTokenPermission')}} />
      7. {check} @@ -996,13 +1062,13 @@ function AccountEditor({ {a.provider === 'nan' && ( <>
      8. - Conecta la cuenta o pega una API key + {t(locale, 'settings.providers.accountEditor.nanTitle')} - +
      9. - Plan - + {t(locale, 'settings.providers.planLabel')} + onChange({ monthlyBudgetUsd: numOrNull(e.target.value) })} />
      10. @@ -1091,12 +1157,15 @@ function AccountEditor({ {session && (
        - Avanzado · carpeta de sesión + {t(locale, 'settings.providers.accountEditor.advanced')}

        - Cada cuenta guarda su sesión en su propia carpeta, así no toca la principal: el login se abre con {session.env}{' '} - apuntando a ella. Si la comprobación pide renovar la sesión, «Volver a abrir el login» abre de nuevo el CLI. + {t(locale, 'settings.providers.accountEditor.sessionNoteBefore')} {session.env}{' '} + {t(locale, 'settings.providers.accountEditor.sessionNoteAfter', { reopen: t(locale, 'settings.providers.cliConnect.reopenLogin') })}

        - Vacío = {defaultSessionDir(a, appName)}. Puedes usar una que ya tengas. Quorb lee {session.file} dentro de ella.}> + {t(locale, 'settings.providers.accountEditor.sessionDirEmpty')} {defaultSessionDir(a, appName)}{t(locale, 'settings.providers.accountEditor.sessionDirMiddle')} {session.file} {t(locale, 'settings.providers.accountEditor.sessionDirEnd')}} + > onChange({ configDir: strOrNull(e.target.value) })} />
        @@ -1104,9 +1173,9 @@ function AccountEditor({
        - Al guardar se borran también sus claves del almacén seguro del sistema. + {t(locale, 'settings.providers.accountEditor.removeHint')}
    ); @@ -2242,6 +2311,7 @@ export function Settings() { {selAccount && ( {info.note}

      `. + cliConnect: { + installTitle: 'Instala {name}', + cliFallback: 'su CLI', + installBody: 'Hace falta para iniciar sesión y para renovarla cuando caduque, aunque ahora funcione.', + sessionFound: 'Fichero de sesión encontrado', + loginOther: 'Inicia sesión con la otra cuenta', + login: 'Inicia sesión', + opensTerminal: 'Se abre una terminal con', + claudeLoginHint: ' (si no te lo pide, escribe', + geminiLoginHint: ' (elige «Login with Google»)', + browserLogin: '. El navegador hace el login oficial y el CLI guarda la sesión, que Quorb solo lee.', + fileExists: ' El fichero existe, pero no confirma que la sesión siga siendo válida: míralo en la comprobación de abajo.', + extraNote: ' Entra con la cuenta que quieres añadir, no con la principal: si el navegador ya tiene abierta la principal, usa una ventana privada.', + reopenLogin: 'Volver a abrir el login', + signIn: 'Iniciar sesión', + saveAndSignIn: 'Guardar e iniciar sesión', + checkAuto: 'Se hace sola en cuanto guardes e inicies sesión.', + }, + // Editor de una cuenta adicional (`AccountEditor`). Los nombres de plan con + // cifra (`Pro · 300`), las opciones de producto (`nan_community`, `GLM Premium`) + // y los textos de enlace de terceros no se traducen. + accountEditor: { + kicker: 'Cuenta adicional', + nameLabel: 'Nombre', + nameHint: 'Se ve en la tira junto al proveedor.', + stepsAria: 'Cómo conectar esta cuenta', + nameStep: 'Ponle nombre', + nameStepBody: 'Arriba, para distinguirla de la principal en la tira (por ejemplo, «Trabajo»).', + checkSave: 'Guarda la cuenta para comprobar que funciona.', + cursorTokenTitle: 'Pega el token de esta cuenta', + cursorTokenBody: 'La app de Cursor guarda una sola sesión (la principal). Entra en cursor.com con esta cuenta y copia la cookie WorkosCursorSessionToken.', + cursorTokenLabel: 'Token de la cuenta', + copilotUserPlan: 'Usuario y plan de GitHub', + githubUser: 'Usuario de GitHub', + planCustom: 'Otra cantidad', + customAllowance: 'Peticiones premium incluidas al mes', + readOnlyToken: 'Token de solo lectura', + githubTokenLabel: 'Token de GitHub de esta cuenta (fine-grained)', + githubTokenCreateIn: 'Créalo con esta cuenta en', + githubTokenPermission: 'con el permiso «Plan» (solo lectura).', + nanTitle: 'Conecta la cuenta o pega una API key', + nanApiKeyAlt: 'API key (alternativa)', + nanPlanHint: 'La URL base y el resto de ajustes avanzados son los de la cuenta principal.', + openrouterHint: 'Puede ser de otra cuenta o de la misma con otro límite.', + createIn: 'Créala en', + apiKeyOf: 'API key de {name}', + adminKeyTitle: 'Admin key de esta organización', + adminKey: 'Admin key', + adminKeyOwn: 'Cada organización tiene su propia', + budgetTitle: 'Presupuesto mensual (opcional)', + budgetLabel: 'USD al mes', + advanced: 'Avanzado · carpeta de sesión', + sessionNoteBefore: 'Cada cuenta guarda su sesión en su propia carpeta, así no toca la principal: el login se abre con', + sessionNoteAfter: 'apuntando a ella. Si la comprobación pide renovar la sesión, «{reopen}» abre de nuevo el CLI.', + sessionDir: 'Carpeta de sesión', + sessionDirEmpty: 'Vacío =', + sessionDirMiddle: '. Puedes usar una que ya tengas. Quorb lee', + sessionDirEnd: 'dentro de ella.', + remove: 'Eliminar esta cuenta', + removeHint: 'Al guardar se borran también sus claves del almacén seguro del sistema.', + }, }, }, // T2c slice 1 (#66): tira del widget y detalle de proveedor. Valores verbatim. diff --git a/src/lib/accounts.ts b/src/lib/accounts.ts index 4eb1a8d..6ae4303 100644 --- a/src/lib/accounts.ts +++ b/src/lib/accounts.ts @@ -1,6 +1,7 @@ // Cuentas adicionales de un proveedor (src-tauri/src/config.rs › Account). import type { Account, AccountProvider, AppConfig, ScopedSecretKey, SecretKey, SecretStatus } from './types'; import { ownedHomeDir } from './app-identity'; +import type { MessageKey } from '../i18n'; /** Proveedor de un id de informe: `claude` o `claude:trabajo` → `claude`. */ export const providerOf = (id: string): string => id.split(':')[0]; @@ -17,11 +18,17 @@ export const SESSION_DIR: Partial> = { +export const CLI_INSTALL: Partial> = { claude: { name: 'Claude Code', cmd: 'irm https://claude.ai/install.ps1 | iex', where: 'PowerShell' }, - codex: { name: 'Codex CLI', cmd: 'npm install -g @openai/codex', where: 'una terminal', needs: 'Node.js' }, - gemini: { name: 'Gemini CLI', cmd: 'npm install -g @google/gemini-cli', where: 'una terminal', needs: 'Node.js' }, + codex: { name: 'Codex CLI', cmd: 'npm install -g @openai/codex', whereKey: 'settings.providers.cliMissing.terminal', needs: 'Node.js' }, + gemini: { name: 'Gemini CLI', cmd: 'npm install -g @google/gemini-cli', whereKey: 'settings.providers.cliMissing.terminal', needs: 'Node.js' }, }; /** Carpeta por defecto (igual que `Account::session_dir`). */ From af9b2713c0be7e0c82fa8960e8b8b281612fd7c3 Mon Sep 17 00:00:00 2001 From: danielgap Date: Thu, 29 Oct 2026 23:29:18 +0100 Subject: [PATCH 2/3] chore(i18n): regenerar el inventario tras C1a (#66) --- docs/i18n-inventory.md | 164 +++++++++++++++++++++++++++++------------ 1 file changed, 116 insertions(+), 48 deletions(-) diff --git a/docs/i18n-inventory.md b/docs/i18n-inventory.md index 1890003..6ba83f7 100644 --- a/docs/i18n-inventory.md +++ b/docs/i18n-inventory.md @@ -1,6 +1,6 @@ # i18n inventory (egdev6/quorb#66) -Generated by `node scripts/i18n-inventory.mjs` from the source trees `src/` = `e59862776cb98564eb2fc5680c5ecb6991629266` and `src-tauri/src/` = `cda8f14546cb8d691b994ef70f9e1696811884bd`. +Generated by `node scripts/i18n-inventory.mjs` from the source trees `src/` = `885c031291a237b5e8f97199076732e54483311d` and `src-tauri/src/` = `cda8f14546cb8d691b994ef70f9e1696811884bd`. Any commit whose `git rev-parse :src` and `:src-tauri/src` match has these exact texts. Do not edit by hand: regenerate, or verify with `node scripts/i18n-inventory.mjs --check`. @@ -18,9 +18,9 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a | Area | Distinct texts | Sites | |---|---:|---:| -| React (`src/`) | 852 | 995 | +| React (`src/`) | 906 | 1042 | | Rust (`src-tauri/src/`) | 405 | 484 | -| Total | 1222 | 1479 | +| Total | 1276 | 1526 | ## src-tauri/src/channel.rs @@ -570,15 +570,11 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - %USERPROFILE%\\.gemini\\oauth_creds.json - %USERPROFILE%\\.grok\\auth.json - (el host por región se cambia en Ajustes avanzados). -- (elige «Login with Google») -- (si no te lo pide, escribe - (solo lectura de la organización). -- . El navegador hace el login oficial y el CLI guarda la sesión, que Quorb solo lee. - . En la región de China, en - . Endpoint no oficial. - . Endpoints no oficiales. - . Para WSL: -- . Puedes usar una que ya tengas. Quorb lee - /login - /v1/usage - 9Router @@ -591,30 +587,21 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - API key de pago por uso (opcional) - Admin API key (sk-ant-admin…) - Admin key -- Admin key de esta organización -- Al guardar se borran también sus claves del almacén seguro del sistema. - Anthropic API -- Arriba, para distinguirla de la principal en la tira (por ejemplo, «Trabajo»). - Avanzado · URL del API - Avanzado · URL, facturación y endpoint de consumo - Avanzado · base de datos de Cursor -- Avanzado · carpeta de sesión - Avanzado · dirección del servidor - Avanzado · fichero de sesión y comando de login - Base URL - Base de datos de Cursor - Business · 300 -- Cada cuenta guarda su sesión en su propia carpeta, así no toca la principal: el login se abre con -- Cada organización tiene su propia -- Carpeta de sesión - ChatGPT (Plus / Pro · Codex) - ChatGPT y Codex - Claude - Claude (Pro / Max) - Comando de login -- Comprobación - Con la cuenta conectada ves lo mismo que en tu dashboard: tokens usados frente a la cuota de cada modelo, cuándo se reinicia y la serie diaria. Sin cuenta, la API key usa -- Conecta la cuenta o pega una API key - Conecta tu cuenta o pega una API key - Conecta tu dashboard local - Conectado @@ -626,9 +613,7 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - Consumo - Contraseña del dashboard - Créala en -- Créalo con esta cuenta en - Créalo en -- Cuenta adicional - Cuenta de NaN Cloud conectada - Cuota de las últimas 5 h, cuota semanal y cuota del ciclo de facturación de la suscripción Go / Go Plus. - Cuota del plan y, si añades la segunda clave, tu saldo de pago por uso. Son @@ -636,7 +621,6 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - Cuotas diarias por modelo. Quorb lee la sesión que guarda - Cursor - Cómo conectar 9Router -- Cómo conectar esta cuenta - Cómo conectar la cuenta - Del panel M/Token en - Desconectar @@ -644,18 +628,14 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - Docs - Día de inicio de facturación - Dónde escucha 9Router (por defecto http://localhost:20128). -- El fichero existe, pero no confirma que la sesión siga siendo válida: míralo en la comprobación de abajo. -- Eliminar esta cuenta - Endpoint de consumo · URL - English - Enterprise · 1000 -- Entra con la cuenta que quieres añadir, no con la principal: si el navegador ya tiene abierta la principal, usa una ventana privada. - Enviar enlace - Español - Etiqueta - Fichero auth.json - Fichero de credenciales -- Fichero de sesión encontrado - Free · 50 - GLM Premium - Gasto del mes y de hoy, límite de la key (si tiene) y saldo de la cuenta. @@ -669,22 +649,12 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - GitHub › Fine-grained tokens - Grok - Grok (SuperGrok) -- Guarda la cuenta para comprobar que funciona. -- Guardar e iniciar sesión -- Guardar y comprobar - Guardar y probar -- Hace falta para iniciar sesión y para renovarla cuando caduque, aunque ahora funcione. - Hoy - Igual que «nan auth login --email»: NaN te manda un enlace de un solo uso. No lo abras en el navegador (se gastaría): cópialo y pégalo aquí. La sesión se guarda en el almacén de credenciales del sistema y da los mismos datos que tu dashboard. -- Inicia sesión -- Inicia sesión con la otra cuenta - Inicia sesión en la app de Cursor -- Iniciar sesión -- Instala - JSON Pointer · límite - JSON Pointer · usado -- La URL base y el resto de ajustes avanzados son los de la cuenta principal. -- La app de Cursor guarda una sola sesión (la principal). Entra en cursor.com con esta cuenta y copia la cookie WorkosCursorSessionToken. - La contraseña del dashboard de 9Router (por defecto «123456» hasta que la cambies). Se guarda en el almacén seguro del sistema. - Las APIs de coste no admiten OAuth: hace falta una - Las claves son por región: global en api.minimax.io, China en api.minimaxi.com. Si el anillo sale con error 1004 o 2049, prueba con la de tu región. @@ -697,7 +667,6 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - Mostrar gasto del mes - NaN Builders - No muestra un sub-orbe hasta que su gasto en el periodo supere esta cifra. 0 = cualquiera que gaste. -- Nombre - Opcional: pinta la barra de gasto del periodo frente al tope que pongas. - OpenAI API - OpenCode Go @@ -708,28 +677,22 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - Pago por uso · API oficial - Pago por uso · Admin key - Pasarela local · uso agregado -- Pega el token de esta cuenta - Periodo de uso - Periodo y desglose - Peticiones premium del mes con la API de facturación de GitHub. Se reinician el día 1 (UTC). - Peticiones premium incluidas al mes - Plan -- Ponle nombre - Presupuesto (USD) - Presupuesto mensual (opcional) - Pro · 300 - Pro+ · 1500 - Probar la API de NaN con la key -- Puede ser de otra cuenta o de la misma con otro límite. - Quitar - Quorb - Quorb lee el uso y el gasto que - Quorb lee la sesión que guarda la app de Cursor en este equipo (solo lectura). Si no usas la app aquí, pega un token manual: tiene prioridad sobre la sesión de la app. - Rango que muestra el orbe y su historial. -- Se abre una terminal con -- Se hace sola en cuanto guardes e inicies sesión. - Se llama con tu key como Bearer y debe devolver JSON. -- Se ve en la tira junto al proveedor. - Sin ella solo se muestra la cuota del plan; el saldo es opcional y su fallo no lo degrade. - Solo para GLM 5.3 (cuenta por periodo de Stripe) cuando usas la API key. - Suborbes ocultos de 9Router @@ -745,8 +708,6 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - Te hemos enviado un enlace. Cópialo (sin abrirlo) y pégalo aquí. - Todo - Token de GitHub (fine-grained) -- Token de GitHub de esta cuenta (fine-grained) -- Token de la cuenta - Token de solo lectura - Token manual (opcional) - USD al mes @@ -757,14 +718,10 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - Vacío = - Ver respuestas de /v1/models y /v1/usage - Volver -- Volver a abrir el login - Wayland - \\\\wsl.localhost\\Ubuntu\\home\\TU_USUARIO\\.claude\\.credentials.json -- apuntando a ella. Si la comprobación pide renovar la sesión, «Volver a abrir el login» abre de nuevo el CLI. - cloud.nan.builders - con el permiso de cuenta «Plan»: solo lectura. -- con el permiso «Plan» (solo lectura). -- dentro de ella. - dos credenciales distintas - egdev.es - egdev6 @@ -778,7 +735,6 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - openrouter.ai › Keys - p. ej. /usage/quota_tokens - p. ej. /usage/used_tokens -- su CLI - tu@correo.com - y las cuotas publicadas. - ya registra en tu equipo: peticiones, tokens y coste por proveedor del periodo. Además del orbe agregado, cada proveedor que haya visto (Claude, Kiro, GLM…) aparece como su propia cuenta. @@ -847,8 +803,12 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a ## src/i18n/en.ts +- (choose “Login with Google”) +- (if it does not ask, type - (needs {needs}) - + Add account +- . The browser does the official login and the CLI stores the session, which Quorb only reads. +- . You can use one you already have. Quorb reads - 1 reset available - 1 · When the limit is exceeded - 2 · When it resets @@ -860,14 +820,23 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - A new version is available: {version}. - A single ring per subscription: the closest limit. - A step is missing +- A terminal opens with +- API key +- API key (alternative) - APIs and credits - Above the rest of the windows. +- Above, to tell it apart from the main one on the strip (for example, “Work”). - Accent color +- Account token - Accounts - Action - Actions - Active - Active but not set up +- Additional account +- Admin key +- Admin key for this organization +- Advanced · session folder - Alert when reaching - Alert when the exceeded limit resets - Alerts @@ -884,6 +853,7 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - Budget - Change - ChatGPT and Codex +- Check - Check every - Check for updates automatically - Check now @@ -901,6 +871,7 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - Configuration - Configure - Connect +- Connect the account or paste an API key - Consumption pace - Copied - Copied ✓ @@ -912,6 +883,8 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - Could not read “Start at login”: - Could not save the language - Could not update Quorb +- Create it at +- Create it with this account at - Current version - Custom effect - Custom effect (video or animated image) @@ -924,8 +897,11 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - Disabled - Done - Drag: snaps to the nearest edge +- Each account keeps its session in its own folder, so it does not touch the main one: the login opens with +- Each organization has its own - Edge - Effect +- Empty = - Enable alerts - Enable shortcuts - Error @@ -934,18 +910,26 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - File - Full screen - GLM Coding Plan · 5 h, weekly quotas and web tools +- GitHub token for this account (fine-grained) +- GitHub user and plan +- GitHub username - Global shortcuts - Go / Go Plus · 5 h, weekly and billing-cycle quotas - Hide on its own - History - History range +- How to connect this account - In addition to the on-screen effect. - Install and restart +- Install {name} - Installed version - Installed version, automatic updates and the project. - Installing +- It can be from another account or from the same one with a different limit. - It is downloaded, the signature is verified and Quorb restarts. +- It is needed to sign in and to renew the session when it expires, even if it works now. - It is the one on the outer ring and the first bar; the other two tones come out on their own. Alerts and overflow keep their alert colors. +- It runs on its own as soon as you save and sign in. - Key expires - Keyboard shortcuts - Keyboard shortcuts that work from any app. @@ -959,6 +943,9 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - Manual updates on macOS. Updates are not checked for or installed from the app. - Medium - Month spend with an admin key +- Monthly budget (optional) +- Name +- Name it - Needs connecting - No file - No known time window to calculate the pace. @@ -972,21 +959,26 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - Official usage ↗ - Once per window (5 h session, week, month…). - Open its panel and press "Renew session". Quorb refreshes itself as soon as it renews. +- Open the login again - Open the most loaded - Open the official CLI to renew the session; the widget refreshes itself when it finishes - Opus +- Other amount - Pace and history - Pace {pace}× · runs out {when} - Pace {pace}× · you will not make it to the reset - Pace {pace}× · you would end at {pct} % - Paste the key here +- Paste this account token - Period through - Personalization not applied - Pick any color (RGB) +- Plan - Plan quota and tokens per day - Plan usage in the cycle and on-demand - Plus / Pro · Codex session and weekly - Position along the edge +- Premium requests included per month - Premium requests of the month - Press a shortcut or “Change” and type the combination. They apply when you save. - Press the combination… (Esc cancels) @@ -1000,8 +992,10 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - Quorb is open source (MIT). If it saves you a scare with the limits, a star on GitHub helps more people find it. - Quorb signs in with its official login. Install it from {where}{needs}; Quorb detects it on its own when it finishes. - Quorb {version} available +- Read-only token - Refresh now - Remove +- Remove this account - Removed - Renew session - Request a provider @@ -1018,11 +1012,16 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - Rings - Save - Save and check +- Save and sign in - Save changes +- Save the account to check that it works. - Save the changes to check this account. - Saved to the system secure store - Saved ✓ +- Saving also deletes its keys from the system secure store. - Saving… +- Session file found +- Session folder - Set up - Settings - Settings not saved: {error} @@ -1031,9 +1030,13 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - Shortcut - Shortcuts - Show Quorb +- Show in the widget - Show or hide the strip - Show resets on the circles +- Shown on the strip next to the provider. - Sign in +- Sign in with the account you want to add, not the main one: if the browser already has the main one open, use a private window. +- Sign in with the other account - Signed out - Size - Small @@ -1051,6 +1054,9 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - System notification - Test - The 9Router sub-providers are discovered automatically from its usage history; they are not added by hand. +- The Cursor app stores a single session (the main one). Sign in at cursor.com with this account and copy the WorkosCursorSessionToken cookie. +- The base URL and the other advanced settings are those of the main account. +- The file exists, but it does not confirm the session is still valid: check the verification below. - The month has just started - The resets balance always stays available in the detail panel. - The window has just started @@ -1064,6 +1070,7 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - Top - Transparency - Tray icon +- USD per month - Up to date - Update now - Update to Quorb {version} @@ -1091,9 +1098,12 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - in {h} h - in {h} h {m} min - in {mins} min +- inside it. +- its CLI - max. {value} - no known limits: - no shortcut +- pointing to it. If the check asks you to renew the session, “{reopen}” opens the CLI again. - resets {when} - scale 0–{max} % - the native geometry could not be read @@ -1102,10 +1112,12 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - the window manager did not grant the size - the window manager did not place the strip where it was asked - with limits: +- with the “Plan” permission (read-only). - {count} resets available - {day}: {value} tokens - {label} runs out{when}, before the reset. - {label}: {value}. Press to change it +- {name} API key - {name} details - {name} is missing on this computer. - {name}: session expired @@ -1117,8 +1129,12 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a ## src/i18n/es.ts +- (elige «Login with Google») - (necesita {needs}) +- (si no te lo pide, escribe - + Añadir cuenta +- . El navegador hace el login oficial y el CLI guarda la sesión, que Quorb solo lee. +- . Puedes usar una que ya tengas. Quorb lee - 1 reset disponible - 1 · Al superar el límite - 2 · Al reiniciarse @@ -1127,6 +1143,9 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - 7 días - ; si la firma no coincide, no se instalan. - A este ritmo +- API key +- API key (alternativa) +- API key de {name} - APIs y créditos - Abajo - Abre el CLI oficial, que renueva la sesión; el widget se actualiza solo al terminar @@ -1146,9 +1165,12 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - Actualizar a Quorb {version} - Actualizar ahora - Además del efecto en pantalla. +- Admin key +- Admin key de esta organización - Ajustes - Al arrancar y cada 6 h. Si hay una nueva, aparece un icono de descarga en la tira. - Al día +- Al guardar se borran también sus claves del almacén seguro del sistema. - Al ritmo actual: {amount} a fin de mes - Alertas - Anillos @@ -1159,11 +1181,13 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - Arranque al iniciar sesión - Arrastrar: se pega al borde más cercano - Arriba +- Arriba, para distinguirla de la principal en la tira (por ejemplo, «Trabajo»). - Atajo - Atajos - Atajos de teclado - Atajos globales - Atención +- Avanzado · carpeta de sesión - Avisar al llegar a - Avisar cuando el límite superado se reinicia - Aún no hay lecturas guardadas. @@ -1172,9 +1196,12 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - Buscando… - Buscar actualizaciones solo - Buscar ahora +- Cada cuenta guarda su sesión en su propia carpeta, así no toca la principal: el login se abre con +- Cada organización tiene su propia - Caducidad de la clave comunicada por el proveedor - Cambiar - Cargando… +- Carpeta de sesión - Cerrar - Cerrar la configuración - ChatGPT y Codex @@ -1183,9 +1210,11 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - Color de acento - Colores resultantes - Combinaciones de teclado que funcionan desde cualquier app. +- Comprobación - Comprobando la conexión… - Comprobar ahora - Con sonido +- Conecta la cuenta o pega una API key - Conectar - Confeti - Configuración @@ -1201,12 +1230,16 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - Copiado ✓ - Copiar - Copiar resumen +- Créala en +- Créalo con esta cuenta en +- Cuenta adicional - Cuentas - Cuota del plan M/Token y saldo de pago por uso - Cuota del plan y tokens por día - Cuota diaria por modelo - Cuándo avisar - Círculos pequeños +- Cómo conectar esta cuenta - Datos del último ciclo correcto - Datos recibidos - Deja una franja en el borde y vuelve al acercar el ratón o cuando hay un aviso. @@ -1216,11 +1249,14 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - Efecto - Efecto propio - Efecto propio (vídeo o imagen animada) +- El fichero existe, pero no confirma que la sesión siga siendo válida: míralo en la comprobación de abajo. - El saldo de resets siempre sigue disponible en el panel de detalle. - Elegir cualquier color (RGB) - Elegir… - Eliminada +- Eliminar esta cuenta - Eliminar key +- Entra con la cuenta que quieres añadir, no con la principal: si el navegador ya tiene abierta la principal, usa una ventana privada. - Error - Es el del anillo exterior y la primera barra; los otros dos tonos salen solos. Los avisos y el exceso conservan sus colores de alerta. - Expandir el menú @@ -1228,6 +1264,7 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - Falta completar un paso - Falta conectar - Falta {name} en este equipo. +- Fichero de sesión encontrado - Fin del periodo comunicado por el proveedor; no confirma cancelación - Funciona - Funcionan aunque Quorb no tenga el foco. @@ -1236,29 +1273,38 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - Gasto del mes con clave de admin - Glow de peligro - Go / Go Plus · cuotas de 5 h, semanal y ciclo de facturación +- Guarda la cuenta para comprobar que funciona. - Guarda los cambios para comprobar esta cuenta. - Guardada en el almacén seguro del sistema - Guardado ✓ - Guardando… - Guardar - Guardar cambios +- Guardar e iniciar sesión - Guardar y comprobar +- Hace falta para iniciar sesión y para renovarla cuando caduque, aunque ahora funcione. - Hay una versión nueva: {version}. - Historial - Historial de consumo - Icono de bandeja - Idioma +- Inicia sesión +- Inicia sesión con la otra cuenta - Iniciar sesión - Inicio al iniciar sesión guardado - Inicio al iniciar sesión no guardado: {error} +- Instala {name} - Instalando - Instalar y reiniciar - Izquierda +- La URL base y el resto de ajustes avanzados son los de la cuenta principal. +- La app de Cursor guarda una sola sesión (la principal). Entra en cursor.com con esta cuenta y copia la cookie WorkosCursorSessionToken. - Listo - Llegan firmadas desde - Los subproveedores de 9Router se descubren solos desde su historial de uso; no se añaden a mano. - Mediano - Mostrar Quorb +- Mostrar en el widget - Mostrar resets en los círculos - Mostrar u ocultar la tira - Ninguno @@ -1270,30 +1316,38 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - No se pudo determinar el modo de actualización. Reinicia Quorb para reintentar. - No se pudo guardar el idioma - No se pudo leer «Arrancar al iniciar sesión»: +- Nombre - Notificaciones - Notificación del sistema - Ocultar sola - Opacidad de la tira y el panel - Opus +- Otra cantidad - Pantalla completa - Para cambiar el orden de los anillos, arrástralos en la tira. - Pasarela local · uso y gasto agregados - Pedir un proveedor - Pega aquí la key +- Pega el token de esta cuenta - Pequeño - Periodo hasta - Personalización no aplicada - Peticiones - Peticiones premium del mes +- Peticiones premium incluidas al mes +- Plan - Plus / Pro · sesión y semanal de Codex +- Ponle nombre - Por encima del resto de ventanas. - Posición en el borde - Presupuesto +- Presupuesto mensual (opcional) - Principal - Pro / Max · sesión de 5 h, semanal y por modelo - Probar - Proveedores - Proyecto +- Puede ser de otra cuenta o de la misma con otro límite. - Pulsa el icono de descarga de la tira para actualizar. - Pulsa la combinación… (Esc cancela) - Pulsa un atajo o «Cambiar» y teclea la combinación. Se aplican al guardar. @@ -1316,9 +1370,12 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - Ritmo {pace}× · no llegas al reinicio - Ritmo {pace}× · se agota {when} - Saldo y gasto por clave +- Se abre una terminal con - Se aplica al momento en todas las ventanas. - Se descarga, se comprueba la firma y Quorb se reinicia. +- Se hace sola en cuanto guardes e inicies sesión. - Se reinicia {when} +- Se ve en la tira junto al proveedor. - Secciones de ajustes - Semana - Sesión de escritorio: {session} @@ -1339,24 +1396,35 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - Tiempo transcurrido de la ventana - Tienes cambios sin guardar: la comprobación usa lo guardado. - Tira +- Token de GitHub de esta cuenta (fine-grained) +- Token de la cuenta +- Token de solo lectura - Tokens - Tokens por día - Tokens por día · 30 días - Transparencia +- USD al mes - Un solo anillo por suscripción: el límite más cercano. - Un vídeo WebM (VP9) con canal alfa o una imagen animada GIF, WebP o APNG con transparencia. Se muestra centrado encima de todo, sin bloquear el ratón; el vídeo dura lo que dure (máx. 20 s) y las imágenes 4,5 s. - Una vez por ventana (sesión de 5 h, semana, mes…). - Usar {color} - Uso del plan en el ciclo y on-demand - Uso oficial ↗ +- Usuario de GitHub +- Usuario y plan de GitHub +- Vacío = - Versión actual - Versión instalada - Versión instalada, actualizaciones automáticas y el proyecto. - Vista previa a {size} px +- Volver a abrir el login - Vídeo o imagen animada +- apuntando a ella. Si la comprobación pide renovar la sesión, «{reopen}» abre de nuevo el CLI. - caché - cada {mins} min +- con el permiso «Plan» (solo lectura). - con límites: +- dentro de ella. - el gestor no colocó la tira donde se pidió - el gestor no concedió el tamaño - en {d} d @@ -1374,6 +1442,7 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - saldo comunicado por z.ai; cada reset vence y se usa manualmente - sin atajo - sin límites conocidos: +- su CLI - una terminal - {count} resets disponibles - {day}: {value} tokens @@ -1396,7 +1465,6 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - irm https://claude.ai/install.ps1 \| iex - npm install -g @google/gemini-cli - npm install -g @openai/codex -- una terminal ## src/lib/alerts.ts From 763d1ce68993aa60be530d2358d135c64b5d1584 Mon Sep 17 00:00:00 2001 From: egdev6 Date: Thu, 29 Oct 2026 23:50:25 +0100 Subject: [PATCH 3/3] docs: regenerar el inventario i18n tras integrar main (#66) --- docs/i18n-inventory.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/i18n-inventory.md b/docs/i18n-inventory.md index 6ba83f7..1a03257 100644 --- a/docs/i18n-inventory.md +++ b/docs/i18n-inventory.md @@ -1,6 +1,6 @@ # i18n inventory (egdev6/quorb#66) -Generated by `node scripts/i18n-inventory.mjs` from the source trees `src/` = `885c031291a237b5e8f97199076732e54483311d` and `src-tauri/src/` = `cda8f14546cb8d691b994ef70f9e1696811884bd`. +Generated by `node scripts/i18n-inventory.mjs` from the source trees `src/` = `a03110502887df4b7126120c13f164bc9a812e9a` and `src-tauri/src/` = `8dca82fa4e3ed90f594534a2e6a9cfcc2321c432`. Any commit whose `git rev-parse :src` and `:src-tauri/src` match has these exact texts. Do not edit by hand: regenerate, or verify with `node scripts/i18n-inventory.mjs --check`. @@ -18,9 +18,9 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a | Area | Distinct texts | Sites | |---|---:|---:| -| React (`src/`) | 906 | 1042 | -| Rust (`src-tauri/src/`) | 405 | 484 | -| Total | 1276 | 1526 | +| React (`src/`) | 908 | 1044 | +| Rust (`src-tauri/src/`) | 409 | 488 | +| Total | 1281 | 1532 | ## src-tauri/src/channel.rs @@ -439,6 +439,7 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a ## src-tauri/src/widget_placement.rs - Invalid widget placement scale: {} +- Wayland - Widget bounds UI callback was dropped - the native window could not be read - the native window origin could not be read @@ -527,8 +528,10 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - Click-through del widget no implementado: el ratón no atraviesa la tira; solo el overlay de efectos lo usa. - El compositor aplica transparencia. - El icono de bandeja se muestra en los gestores que la evalúan. +- El posicionamiento absoluto de ventanas no está disponible bajo xdg-shell en Wayland: el arrastre de la tira se delega al compositor. - El registro puede denegarse en Wayland: el error se muestra en Ajustes › Atajos. - El soporte de siempre-encima depende del compositor; si la tira se oculta, usa «Auto-hide». +- En X11 (y bajo XWayland con GDK_BACKEND=x11) el anclaje al borde y el arrastre con imán funcionan; la tira alcanza el borde del área de trabajo (#205). - En X11 la tira se mantiene sobre las demás ventanas. - GNOME Wayland (Mutter) puede ignorar hints de always-on-top en algunos escenarios. La tira recurre a posición fija + skip_taskbar. - Host StatusNotifier detectado en la sesión: hay dónde mostrar el icono. La extensión «AppIndicator» de Ubuntu está habilitada. Solo se acredita la disponibilidad del host, no la visibilidad del icono. @@ -544,6 +547,7 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - Los atajos globales dependen del registro por portal; si falla, el error se ve en Ajustes › Atajos. - Los atajos globales funcionan desde cualquier app. - Mutter admite hints de siempre-encima de forma parcial; si la tira se oculta tras ventanas, usa «Auto-hide». +- Mutter no expone posicionamiento absoluto de ventanas (gtk_window_move no tiene efecto): el arrastre usa el gesto del compositor y, al soltar, no hay imán ni se persiste la posición. - Mutter puede ignorar la región de input: el ratón puede quedar capturado en el hueco; prueba a mover la tira. - No se detectó ningún host StatusNotifier en la sesión: no hay dónde mostrar el icono de bandeja. La extensión «AppIndicator» está habilitada, pero la barra activa no provee el host (¿el dock fue reemplazado?). - No se detectó ningún host StatusNotifier en la sesión: no hay dónde mostrar el icono. Instalá la extensión «AppIndicator» de Ubuntu para proveerlo. @@ -1085,6 +1089,7 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - What gets checked and how each provider connects. - What happens when a limit is exceeded and when it resets. - When to alert +- Window positioning - With sound - Works - You can also drag it from its handle: it sticks to the closest edge. @@ -1339,6 +1344,7 @@ tuples (e.g. the `"req"` unit in `providers/nan.rs`) are only listed when they a - Plus / Pro · sesión y semanal de Codex - Ponle nombre - Por encima del resto de ventanas. +- Posicionamiento de la ventana - Posición en el borde - Presupuesto - Presupuesto mensual (opcional)