Rework login modal: translations, layout, and UX

Overhaul AuthDialog's login step:
- Add translation keys for every previously-hardcoded string (titles,
  buttons, placeholders, status labels, validation/errors) across all
  sixteen locales.
- The secret-key form is no longer collapsible — it's always open and is
  the first option, followed by 'Log in with extension', then a
  text-with-arrow link to the remote-signer step.
- Move the key-file upload icon onto the same row as the nsec input
  (instead of beside the submit button); submit button is now full-width
  below.
- Surface extension-login errors as a destructive toast rather than
  writing them into the nsec input's inline error.
- Replace the centered 'Back' text buttons with a back arrow in the
  top-left of the dialog header.

Also correct AGENTS.md's i18n section: the project ships fifteen
non-English locales (hi, id, sw, tr, zh-Hant were missing from the list).
This commit is contained in:
lemon
2026-06-21 00:08:52 -07:00
parent b01f573bf9
commit 97f5f82b05
18 changed files with 560 additions and 142 deletions
+3 -3
View File
@@ -266,16 +266,16 @@ The router provides automatic scroll-to-top on navigation and a 404 `NotFound` p
## Internationalization ## Internationalization
All user-facing strings live in `src/locales/<lang>.json`. `en.json` is the source of truth; ten other locales ship alongside it: `ar`, `es`, `fa`, `fr`, `km`, `ps`, `pt`, `ru`, `sn`, `zh`. All user-facing strings live in `src/locales/<lang>.json`. `en.json` is the source of truth; fifteen other locales ship alongside it: `ar`, `es`, `fa`, `fr`, `hi`, `id`, `km`, `ps`, `pt`, `ru`, `sn`, `sw`, `tr`, `zh`, `zh-Hant`.
**When you edit, add, or remove a translated string, update every locale in the same change — not just `en.json`.** Leaving the other locales stale ships an inconsistent app: users in other languages either see outdated copy or get an English fallback in the middle of a localized screen. This applies to FAQ entries, guide bodies, button labels, error messages — every value reachable through `t()`. **When you edit, add, or remove a translated string, update every locale in the same change — not just `en.json`.** Leaving the other locales stale ships an inconsistent app: users in other languages either see outdated copy or get an English fallback in the middle of a localized screen. This applies to FAQ entries, guide bodies, button labels, error messages — every value reachable through `t()`.
Concrete rules: Concrete rules:
- **Edits to an existing key** — change the value in `en.json` first, then update the corresponding key in all ten other locales. Translate the new content into each language; don't paste English. Preserve `{{interpolation}}` placeholders, markdown links, and technical tokens (`sp1…`, `BIP-352`, kind numbers, etc.) verbatim. - **Edits to an existing key** — change the value in `en.json` first, then update the corresponding key in all fifteen other locales. Translate the new content into each language; don't paste English. Preserve `{{interpolation}}` placeholders, markdown links, and technical tokens (`sp1…`, `BIP-352`, kind numbers, etc.) verbatim.
- **New keys** — add to `en.json` first, then add the same key with a translated value in every other locale. `src/test/locales.test.ts` fails the build if any locale ships a key that doesn't exist in `en.json`, but the inverse (a key missing from a non-English locale) is allowed and falls back to English at runtime — which is exactly the user-visible mess you're trying to avoid. - **New keys** — add to `en.json` first, then add the same key with a translated value in every other locale. `src/test/locales.test.ts` fails the build if any locale ships a key that doesn't exist in `en.json`, but the inverse (a key missing from a non-English locale) is allowed and falls back to English at runtime — which is exactly the user-visible mess you're trying to avoid.
- **Removed keys** — delete from `en.json` and every other locale together. Leftover keys are dead translations and clutter future diffs. - **Removed keys** — delete from `en.json` and every other locale together. Leftover keys are dead translations and clutter future diffs.
- **Parallelize the translation work** — when updating one English string across all ten locales, dispatch the per-language edits to subagents in parallel rather than translating ten files sequentially. Provide each subagent the new English source, the existing translation snippet (so it matches established voice), and explicit instructions to preserve placeholders and technical tokens. - **Parallelize the translation work** — when updating one English string across all fifteen locales, dispatch the per-language edits to subagents in parallel rather than translating fifteen files sequentially. Provide each subagent the new English source, the existing translation snippet (so it matches established voice), and explicit instructions to preserve placeholders and technical tokens.
Always run `npm run test` after locale changes — `locales.test.ts` catches structural drift, and the wider suite catches any `t()` calls that referenced a key you renamed. Always run `npm run test` after locale changes — `locales.test.ts` catches structural drift, and the wider suite catches any `t()` calls that referenced a key you renamed.
+109 -123
View File
@@ -3,12 +3,14 @@ import {
Upload, Upload,
ChevronDown, ChevronDown,
ChevronUp, ChevronUp,
ArrowLeft,
ArrowRight,
Loader2, Loader2,
ExternalLink, ExternalLink,
} from 'lucide-react'; } from 'lucide-react';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'; import { Dialog, DialogContent, DialogTitle } from '@/components/ui/dialog';
import { import {
Collapsible, Collapsible,
CollapsibleContent, CollapsibleContent,
@@ -26,8 +28,10 @@ import {
type NostrConnectStatus, type NostrConnectStatus,
} from '@/hooks/useLoginActions'; } from '@/hooks/useLoginActions';
import { useIsMobile } from '@/hooks/useIsMobile'; import { useIsMobile } from '@/hooks/useIsMobile';
import { useToast } from '@/hooks/useToast';
import { useOnboarding } from '@/contexts/onboardingContextDef'; import { useOnboarding } from '@/contexts/onboardingContextDef';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { cn } from '@/lib/utils';
interface AuthDialogProps { interface AuthDialogProps {
isOpen: boolean; isOpen: boolean;
@@ -45,17 +49,6 @@ type Step = 'welcome' | 'login' | 'connect';
const validateNsec = (nsec: string) => /^nsec1[a-zA-Z0-9]{58}$/.test(nsec); const validateNsec = (nsec: string) => /^nsec1[a-zA-Z0-9]{58}$/.test(nsec);
const validateBunkerUri = (uri: string) => uri.startsWith('bunker://'); const validateBunkerUri = (uri: string) => uri.startsWith('bunker://');
const connectStatusLabel = (status: NostrConnectStatus | null): string => {
switch (status) {
case 'awaiting-connect':
return 'Waiting for signer connection…';
case 'getting-public-key':
return 'Getting public key…';
default:
return '';
}
};
/** Check if running on an actual mobile device (not just a small screen). */ /** Check if running on an actual mobile device (not just a small screen). */
function isMobileDevice(): boolean { function isMobileDevice(): boolean {
if (typeof navigator === 'undefined') return false; if (typeof navigator === 'undefined') return false;
@@ -69,7 +62,6 @@ const AuthDialog: React.FC<AuthDialogProps> = ({ isOpen, onClose }) => {
const [loginNsec, setLoginNsec] = useState(''); const [loginNsec, setLoginNsec] = useState('');
const [isLoggingIn, setIsLoggingIn] = useState(false); const [isLoggingIn, setIsLoggingIn] = useState(false);
const [loginError, setLoginError] = useState(''); const [loginError, setLoginError] = useState('');
const [showMoreOptions, setShowMoreOptions] = useState(false);
// Nostrconnect / bunker state // Nostrconnect / bunker state
const [nostrConnectParams, setNostrConnectParams] = useState<NostrConnectParams | null>(null); const [nostrConnectParams, setNostrConnectParams] = useState<NostrConnectParams | null>(null);
@@ -93,7 +85,19 @@ const AuthDialog: React.FC<AuthDialogProps> = ({ isOpen, onClose }) => {
const login = useLoginActions(); const login = useLoginActions();
const { config } = useAppContext(); const { config } = useAppContext();
const { startSignup } = useOnboarding(); const { startSignup } = useOnboarding();
const { toast } = useToast();
const { t } = useTranslation(); const { t } = useTranslation();
const connectStatusLabel = (status: NostrConnectStatus | null): string => {
switch (status) {
case 'awaiting-connect':
return t('auth.waitingForSigner');
case 'getting-public-key':
return t('auth.gettingPublicKey');
default:
return '';
}
};
// Stable refs so the nostrconnect listening effect below doesn't restart on // Stable refs so the nostrconnect listening effect below doesn't restart on
// every parent render. Parents typically pass inline arrow functions for // every parent render. Parents typically pass inline arrow functions for
// onClose, and useLoginActions returns a fresh object each render — without // onClose, and useLoginActions returns a fresh object each render — without
@@ -125,7 +129,6 @@ const AuthDialog: React.FC<AuthDialogProps> = ({ isOpen, onClose }) => {
setLoginNsec(''); setLoginNsec('');
setIsLoggingIn(false); setIsLoggingIn(false);
setLoginError(''); setLoginError('');
setShowMoreOptions(false);
setNostrConnectParams(null); setNostrConnectParams(null);
setNostrConnectUri(''); setNostrConnectUri('');
setConnectError(null); setConnectError(null);
@@ -228,7 +231,7 @@ const AuthDialog: React.FC<AuthDialogProps> = ({ isOpen, onClose }) => {
await login.bunker(bunkerUri); await login.bunker(bunkerUri);
onClose(); onClose();
} catch { } catch {
setConnectError('Failed to connect. Check the bunker URI.'); setConnectError(t('auth.errorBunkerConnect'));
setIsLoggingIn(false); setIsLoggingIn(false);
} }
}; };
@@ -255,11 +258,11 @@ const AuthDialog: React.FC<AuthDialogProps> = ({ isOpen, onClose }) => {
// Login: submit the entered nsec. // Login: submit the entered nsec.
const handleLogin = () => { const handleLogin = () => {
if (!loginNsec.trim()) { if (!loginNsec.trim()) {
setLoginError('Enter your secret key.'); setLoginError(t('auth.errorEnterSecretKey'));
return; return;
} }
if (!validateNsec(loginNsec)) { if (!validateNsec(loginNsec)) {
setLoginError('Invalid secret key. Must start with nsec1.'); setLoginError(t('auth.errorInvalidSecretKey'));
return; return;
} }
@@ -271,7 +274,7 @@ const AuthDialog: React.FC<AuthDialogProps> = ({ isOpen, onClose }) => {
login.nsec(loginNsec); login.nsec(loginNsec);
onClose(); onClose();
} catch { } catch {
setLoginError("Couldn't log in with this key."); setLoginError(t('auth.errorLoginFailed'));
setIsLoggingIn(false); setIsLoggingIn(false);
} }
}, 50); }, 50);
@@ -287,10 +290,10 @@ const AuthDialog: React.FC<AuthDialogProps> = ({ isOpen, onClose }) => {
if (content && validateNsec(content.trim())) { if (content && validateNsec(content.trim())) {
setLoginNsec(content.trim()); setLoginNsec(content.trim());
} else { } else {
setLoginError('File does not contain a valid secret key.'); setLoginError(t('auth.errorFileNoKey'));
} }
}; };
reader.onerror = () => setLoginError('Failed to read file.'); reader.onerror = () => setLoginError(t('auth.errorFileRead'));
reader.readAsText(file); reader.readAsText(file);
}; };
@@ -301,7 +304,11 @@ const AuthDialog: React.FC<AuthDialogProps> = ({ isOpen, onClose }) => {
await login.extension(); await login.extension();
onClose(); onClose();
} catch (e) { } catch (e) {
setLoginError(e instanceof Error ? e.message : 'Extension login failed.'); toast({
variant: 'destructive',
title: t('auth.extensionErrorTitle'),
description: e instanceof Error ? e.message : t('auth.errorExtensionFailed'),
});
setIsLoggingIn(false); setIsLoggingIn(false);
} }
}; };
@@ -309,9 +316,9 @@ const AuthDialog: React.FC<AuthDialogProps> = ({ isOpen, onClose }) => {
const getTitle = () => { const getTitle = () => {
switch (step) { switch (step) {
case 'login': case 'login':
return 'Log in'; return t('auth.loginTitle');
case 'connect': case 'connect':
return 'Connect signer'; return t('auth.connectSignerTitle');
default: default:
return ''; return '';
} }
@@ -367,85 +374,57 @@ const AuthDialog: React.FC<AuthDialogProps> = ({ isOpen, onClose }) => {
</div> </div>
) : ( ) : (
<> <>
<DialogHeader className="px-6 pt-6"> <div className="relative px-6 pt-6">
<DialogTitle className="text-lg font-semibold leading-none tracking-tight text-center"> <button
type="button"
onClick={() => setStep(step === 'connect' ? 'login' : 'welcome')}
aria-label={t('auth.back')}
className="absolute left-4 top-5 size-9 rounded-full flex items-center justify-center text-muted-foreground hover:text-foreground hover:bg-secondary motion-safe:transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary"
>
<ArrowLeft className="size-5 rtl:rotate-180" />
</button>
<DialogTitle className="text-lg font-semibold leading-none tracking-tight text-center px-9">
{getTitle()} {getTitle()}
</DialogTitle> </DialogTitle>
</DialogHeader> </div>
<div className="px-6 pb-6 pt-4 space-y-5"> <div className="px-6 pb-6 pt-4 space-y-5">
{/* Login step. */} {/* Login step. The secret-key form is always visible and first,
followed by the extension button (when available), then a
text link out to the remote-signer (connect) step. */}
{step === 'login' && ( {step === 'login' && (
<div className="space-y-4"> <div className="space-y-4">
{hasExtension ? ( <NsecLoginForm
<> loginNsec={loginNsec}
<Button setLoginNsec={setLoginNsec}
onClick={handleExtensionLogin} loginError={loginError}
disabled={isLoggingIn} setLoginError={setLoginError}
className="w-full h-12" isLoggingIn={isLoggingIn}
> onSubmit={handleLogin}
{isLoggingIn ? 'Logging in…' : 'Log in with extension'} onFileChange={handleFileUpload}
</Button> fileInputRef={fileInputRef}
t={t}
/>
<Button {hasExtension && (
variant="outline" <Button
onClick={goToConnect} variant="outline"
className="w-full h-12" onClick={handleExtensionLogin}
> disabled={isLoggingIn}
Use remote signer className="w-full h-12"
</Button> >
{isLoggingIn ? t('auth.loggingIn') : t('auth.loginWithExtension')}
<Collapsible open={showMoreOptions} onOpenChange={setShowMoreOptions}> </Button>
<CollapsibleTrigger className="w-full flex items-center justify-center gap-1 text-sm text-muted-foreground hover:text-foreground py-2">
<span>Use secret key</span>
<ChevronDown
className={`w-4 h-4 transition-transform ${
showMoreOptions ? 'rotate-180' : ''
}`}
/>
</CollapsibleTrigger>
<CollapsibleContent className="space-y-3 pt-1">
<NsecLoginForm
loginNsec={loginNsec}
setLoginNsec={setLoginNsec}
loginError={loginError}
setLoginError={setLoginError}
isLoggingIn={isLoggingIn}
onSubmit={handleLogin}
onFileChange={handleFileUpload}
fileInputRef={fileInputRef}
/>
</CollapsibleContent>
</Collapsible>
</>
) : (
<>
<NsecLoginForm
loginNsec={loginNsec}
setLoginNsec={setLoginNsec}
loginError={loginError}
setLoginError={setLoginError}
isLoggingIn={isLoggingIn}
onSubmit={handleLogin}
onFileChange={handleFileUpload}
fileInputRef={fileInputRef}
/>
<Button
variant="outline"
onClick={goToConnect}
className="w-full"
>
Use remote signer
</Button>
</>
)} )}
<button <button
onClick={() => setStep('welcome')} type="button"
className="w-full text-sm text-muted-foreground hover:text-foreground" onClick={goToConnect}
className="w-full flex items-center justify-center gap-1 text-sm text-muted-foreground hover:text-foreground motion-safe:transition-colors py-2"
> >
Back <span>{t('auth.useRemoteSigner')}</span>
<ArrowRight className="w-4 h-4 rtl:rotate-180" />
</button> </button>
</div> </div>
)} )}
@@ -458,7 +437,7 @@ const AuthDialog: React.FC<AuthDialogProps> = ({ isOpen, onClose }) => {
<div className="flex flex-col items-center space-y-3 py-4"> <div className="flex flex-col items-center space-y-3 py-4">
<p className="text-sm text-destructive text-center">{connectError}</p> <p className="text-sm text-destructive text-center">{connectError}</p>
<Button variant="outline" onClick={handleConnectRetry}> <Button variant="outline" onClick={handleConnectRetry}>
Try again {t('auth.tryAgain')}
</Button> </Button>
</div> </div>
) : showProgressView ? ( ) : showProgressView ? (
@@ -476,7 +455,7 @@ const AuthDialog: React.FC<AuthDialogProps> = ({ isOpen, onClose }) => {
onClick={handleConnectRetry} onClick={handleConnectRetry}
className="text-sm text-primary hover:underline underline-offset-4 font-medium" className="text-sm text-primary hover:underline underline-offset-4 font-medium"
> >
Cancel {t('auth.cancel')}
</button> </button>
</div> </div>
) : nostrConnectUri ? ( ) : nostrConnectUri ? (
@@ -490,7 +469,7 @@ const AuthDialog: React.FC<AuthDialogProps> = ({ isOpen, onClose }) => {
{isMobile && ( {isMobile && (
<Button onClick={handleOpenSignerApp} className="w-full h-12"> <Button onClick={handleOpenSignerApp} className="w-full h-12">
<ExternalLink className="w-5 h-5 mr-2" /> <ExternalLink className="w-5 h-5 mr-2" />
Open signer app {t('auth.openSignerApp')}
</Button> </Button>
)} )}
</> </>
@@ -504,7 +483,7 @@ const AuthDialog: React.FC<AuthDialogProps> = ({ isOpen, onClose }) => {
{/* Manual bunker URI fallback. */} {/* Manual bunker URI fallback. */}
<Collapsible open={showBunkerInput} onOpenChange={setShowBunkerInput}> <Collapsible open={showBunkerInput} onOpenChange={setShowBunkerInput}>
<CollapsibleTrigger className="w-full flex items-center justify-center gap-1 text-sm text-muted-foreground hover:text-foreground py-2"> <CollapsibleTrigger className="w-full flex items-center justify-center gap-1 text-sm text-muted-foreground hover:text-foreground py-2">
<span>Enter bunker URI manually</span> <span>{t('auth.enterBunkerManually')}</span>
{showBunkerInput ? ( {showBunkerInput ? (
<ChevronUp className="w-4 h-4" /> <ChevronUp className="w-4 h-4" />
) : ( ) : (
@@ -515,12 +494,12 @@ const AuthDialog: React.FC<AuthDialogProps> = ({ isOpen, onClose }) => {
<Input <Input
value={bunkerUri} value={bunkerUri}
onChange={(e) => setBunkerUri(e.target.value)} onChange={(e) => setBunkerUri(e.target.value)}
placeholder="bunker://…" placeholder={t('auth.bunkerPlaceholder')}
className="text-base md:text-sm" className="text-base md:text-sm"
/> />
{bunkerUri && !validateBunkerUri(bunkerUri) && ( {bunkerUri && !validateBunkerUri(bunkerUri) && (
<Alert variant="destructive"> <Alert variant="destructive">
<AlertDescription>Invalid bunker URI format.</AlertDescription> <AlertDescription>{t('auth.invalidBunkerFormat')}</AlertDescription>
</Alert> </Alert>
)} )}
<Button <Button
@@ -531,17 +510,10 @@ const AuthDialog: React.FC<AuthDialogProps> = ({ isOpen, onClose }) => {
} }
className="w-full" className="w-full"
> >
{isLoggingIn ? 'Connecting' : 'Connect'} {isLoggingIn ? t('auth.connecting') : t('auth.connect')}
</Button> </Button>
</CollapsibleContent> </CollapsibleContent>
</Collapsible> </Collapsible>
<button
onClick={() => setStep('login')}
className="w-full text-sm text-muted-foreground hover:text-foreground"
>
Back
</button>
</div> </div>
)} )}
</div> </div>
@@ -562,6 +534,7 @@ interface NsecLoginFormProps {
onSubmit: () => void; onSubmit: () => void;
onFileChange: (e: React.ChangeEvent<HTMLInputElement>) => void; onFileChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
fileInputRef: React.RefObject<HTMLInputElement | null>; fileInputRef: React.RefObject<HTMLInputElement | null>;
t: (key: string) => string;
} }
const NsecLoginForm: React.FC<NsecLoginFormProps> = ({ const NsecLoginForm: React.FC<NsecLoginFormProps> = ({
@@ -573,6 +546,7 @@ const NsecLoginForm: React.FC<NsecLoginFormProps> = ({
onSubmit, onSubmit,
onFileChange, onFileChange,
fileInputRef, fileInputRef,
t,
}) => ( }) => (
<form <form
onSubmit={(e) => { onSubmit={(e) => {
@@ -582,27 +556,26 @@ const NsecLoginForm: React.FC<NsecLoginFormProps> = ({
className="space-y-3" className="space-y-3"
data-nsec-allowed data-nsec-allowed
> >
<Input <span className="block text-sm font-medium text-foreground">
type="password" {t('auth.secretKeyLabel')}
value={loginNsec} </span>
onChange={(e) => {
setLoginNsec(e.target.value);
if (loginError) setLoginError('');
}}
placeholder="nsec1…"
autoComplete="off"
className={loginError ? 'border-destructive focus-visible:ring-destructive' : ''}
/>
{loginError && <p className="text-sm text-destructive">{loginError}</p>}
{/* nsec input and the key-file upload icon share a row. */}
<div className="flex gap-2"> <div className="flex gap-2">
<Button <Input
type="submit" type="password"
disabled={isLoggingIn || !loginNsec.trim()} value={loginNsec}
className="flex-1" onChange={(e) => {
> setLoginNsec(e.target.value);
{isLoggingIn ? 'Logging in…' : 'Log in'} if (loginError) setLoginError('');
</Button> }}
placeholder={t('auth.nsecPlaceholder')}
autoComplete="off"
className={cn(
'flex-1',
loginError && 'border-destructive focus-visible:ring-destructive',
)}
/>
<input <input
type="file" type="file"
accept=".txt" accept=".txt"
@@ -614,11 +587,24 @@ const NsecLoginForm: React.FC<NsecLoginFormProps> = ({
type="button" type="button"
variant="outline" variant="outline"
size="icon" size="icon"
className="shrink-0"
aria-label={t('auth.uploadKeyFile')}
title={t('auth.uploadKeyFile')}
onClick={() => fileInputRef.current?.click()} onClick={() => fileInputRef.current?.click()}
> >
<Upload className="w-4 h-4" /> <Upload className="w-4 h-4" />
</Button> </Button>
</div> </div>
{loginError && <p className="text-sm text-destructive">{loginError}</p>}
<Button
type="submit"
disabled={isLoggingIn || !loginNsec.trim()}
className="w-full h-12"
>
{isLoggingIn ? t('auth.loggingIn') : t('auth.login')}
</Button>
</form> </form>
); );
+28 -1
View File
@@ -131,7 +131,34 @@
"logout": "تسجيل الخروج", "logout": "تسجيل الخروج",
"addAccount": "إضافة حساب آخر", "addAccount": "إضافة حساب آخر",
"createNewAccount": "إنشاء حساب جديد", "createNewAccount": "إنشاء حساب جديد",
"loginExisting": "تسجيل الدخول بحساب موجود" "loginExisting": "تسجيل الدخول بحساب موجود",
"loginTitle": "تسجيل الدخول",
"connectSignerTitle": "ربط أداة التوقيع",
"back": "رجوع",
"secretKeyLabel": "استخدام المفتاح السري",
"nsecPlaceholder": "nsec1…",
"uploadKeyFile": "رفع ملف المفتاح",
"loggingIn": "جارٍ تسجيل الدخول…",
"loginWithExtension": "تسجيل الدخول باستخدام إضافة",
"useRemoteSigner": "استخدام أداة توقيع عن بُعد",
"tryAgain": "حاول مرة أخرى",
"cancel": "إلغاء",
"openSignerApp": "فتح تطبيق التوقيع",
"enterBunkerManually": "إدخال رابط البنكر يدويًا",
"bunkerPlaceholder": "bunker://…",
"invalidBunkerFormat": "صيغة رابط البنكر غير صالحة.",
"connecting": "جارٍ الاتصال…",
"connect": "اتصال",
"waitingForSigner": "في انتظار اتصال أداة التوقيع…",
"gettingPublicKey": "جارٍ الحصول على المفتاح العام…",
"errorEnterSecretKey": "أدخل مفتاحك السري.",
"errorInvalidSecretKey": "مفتاح سري غير صالح. يجب أن يبدأ بـ nsec1.",
"errorLoginFailed": "تعذّر تسجيل الدخول بهذا المفتاح.",
"errorFileNoKey": "الملف لا يحتوي على مفتاح سري صالح.",
"errorFileRead": "فشل في قراءة الملف.",
"errorExtensionFailed": "فشل تسجيل الدخول باستخدام الإضافة.",
"errorBunkerConnect": "فشل الاتصال. تحقق من رابط البنكر.",
"extensionErrorTitle": "فشل تسجيل الدخول باستخدام الإضافة"
}, },
"onboarding": { "onboarding": {
"ariaLabel": "إعداد الحساب", "ariaLabel": "إعداد الحساب",
+28 -1
View File
@@ -132,7 +132,34 @@
"logout": "Log out", "logout": "Log out",
"addAccount": "Add another account", "addAccount": "Add another account",
"createNewAccount": "Create new account", "createNewAccount": "Create new account",
"loginExisting": "Log in with existing" "loginExisting": "Log in with existing",
"loginTitle": "Log in",
"connectSignerTitle": "Connect signer",
"back": "Back",
"secretKeyLabel": "Use secret key",
"nsecPlaceholder": "nsec1…",
"uploadKeyFile": "Upload key file",
"loggingIn": "Logging in…",
"loginWithExtension": "Log in with extension",
"useRemoteSigner": "Use a remote signer",
"tryAgain": "Try again",
"cancel": "Cancel",
"openSignerApp": "Open signer app",
"enterBunkerManually": "Enter bunker URI manually",
"bunkerPlaceholder": "bunker://…",
"invalidBunkerFormat": "Invalid bunker URI format.",
"connecting": "Connecting…",
"connect": "Connect",
"waitingForSigner": "Waiting for signer connection…",
"gettingPublicKey": "Getting public key…",
"errorEnterSecretKey": "Enter your secret key.",
"errorInvalidSecretKey": "Invalid secret key. Must start with nsec1.",
"errorLoginFailed": "Couldn't log in with this key.",
"errorFileNoKey": "File does not contain a valid secret key.",
"errorFileRead": "Failed to read file.",
"errorExtensionFailed": "Extension login failed.",
"errorBunkerConnect": "Failed to connect. Check the bunker URI.",
"extensionErrorTitle": "Extension login failed"
}, },
"onboarding": { "onboarding": {
"ariaLabel": "Account setup", "ariaLabel": "Account setup",
+28 -1
View File
@@ -131,7 +131,34 @@
"logout": "Cerrar sesión", "logout": "Cerrar sesión",
"addAccount": "Agregar otra cuenta", "addAccount": "Agregar otra cuenta",
"createNewAccount": "Crear cuenta nueva", "createNewAccount": "Crear cuenta nueva",
"loginExisting": "Iniciar sesión con una existente" "loginExisting": "Iniciar sesión con una existente",
"loginTitle": "Iniciar sesión",
"connectSignerTitle": "Conectar firmante",
"back": "Atrás",
"secretKeyLabel": "Usar clave secreta",
"nsecPlaceholder": "nsec1…",
"uploadKeyFile": "Subir archivo de clave",
"loggingIn": "Iniciando sesión…",
"loginWithExtension": "Iniciar sesión con extensión",
"useRemoteSigner": "Usar un firmante remoto",
"tryAgain": "Intentar de nuevo",
"cancel": "Cancelar",
"openSignerApp": "Abrir aplicación de firma",
"enterBunkerManually": "Introducir URI de bunker manualmente",
"bunkerPlaceholder": "bunker://…",
"invalidBunkerFormat": "Formato de URI de bunker no válido.",
"connecting": "Conectando…",
"connect": "Conectar",
"waitingForSigner": "Esperando la conexión del firmante…",
"gettingPublicKey": "Obteniendo clave pública…",
"errorEnterSecretKey": "Introduce tu clave secreta.",
"errorInvalidSecretKey": "Clave secreta no válida. Debe empezar con nsec1.",
"errorLoginFailed": "No se pudo iniciar sesión con esta clave.",
"errorFileNoKey": "El archivo no contiene una clave secreta válida.",
"errorFileRead": "No se pudo leer el archivo.",
"errorExtensionFailed": "Error al iniciar sesión con la extensión.",
"errorBunkerConnect": "No se pudo conectar. Comprueba el URI del bunker.",
"extensionErrorTitle": "Error al iniciar sesión con la extensión"
}, },
"onboarding": { "onboarding": {
"ariaLabel": "Configuración de la cuenta", "ariaLabel": "Configuración de la cuenta",
+28 -1
View File
@@ -131,7 +131,34 @@
"logout": "خروج", "logout": "خروج",
"addAccount": "افزودن حساب دیگر", "addAccount": "افزودن حساب دیگر",
"createNewAccount": "ایجاد حساب جدید", "createNewAccount": "ایجاد حساب جدید",
"loginExisting": "ورود با حساب موجود" "loginExisting": "ورود با حساب موجود",
"loginTitle": "ورود",
"connectSignerTitle": "اتصال امضاکننده",
"back": "بازگشت",
"secretKeyLabel": "استفاده از کلید مخفی",
"nsecPlaceholder": "nsec1…",
"uploadKeyFile": "بارگذاری فایل کلید",
"loggingIn": "در حال ورود…",
"loginWithExtension": "ورود با افزونه",
"useRemoteSigner": "استفاده از امضاکننده راه دور",
"tryAgain": "تلاش دوباره",
"cancel": "لغو",
"openSignerApp": "باز کردن برنامه امضاکننده",
"enterBunkerManually": "وارد کردن دستی نشانی bunker",
"bunkerPlaceholder": "bunker://…",
"invalidBunkerFormat": "قالب نشانی bunker نامعتبر است.",
"connecting": "در حال اتصال…",
"connect": "اتصال",
"waitingForSigner": "در انتظار اتصال امضاکننده…",
"gettingPublicKey": "در حال دریافت کلید عمومی…",
"errorEnterSecretKey": "کلید مخفی خود را وارد کنید.",
"errorInvalidSecretKey": "کلید مخفی نامعتبر است. باید با nsec1 شروع شود.",
"errorLoginFailed": "ورود با این کلید ممکن نشد.",
"errorFileNoKey": "فایل حاوی کلید مخفی معتبری نیست.",
"errorFileRead": "خواندن فایل ناموفق بود.",
"errorExtensionFailed": "ورود با افزونه ناموفق بود.",
"errorBunkerConnect": "اتصال ناموفق بود. نشانی bunker را بررسی کنید.",
"extensionErrorTitle": "ورود با افزونه ناموفق بود"
}, },
"onboarding": { "onboarding": {
"ariaLabel": "راه‌اندازی حساب", "ariaLabel": "راه‌اندازی حساب",
+28 -1
View File
@@ -131,7 +131,34 @@
"logout": "Se déconnecter", "logout": "Se déconnecter",
"addAccount": "Ajouter un autre compte", "addAccount": "Ajouter un autre compte",
"createNewAccount": "Créer un compte", "createNewAccount": "Créer un compte",
"loginExisting": "Se connecter avec un compte existant" "loginExisting": "Se connecter avec un compte existant",
"loginTitle": "Se connecter",
"connectSignerTitle": "Connecter le signataire",
"back": "Retour",
"secretKeyLabel": "Utiliser la clé secrète",
"nsecPlaceholder": "nsec1…",
"uploadKeyFile": "Importer un fichier de clé",
"loggingIn": "Connexion…",
"loginWithExtension": "Se connecter avec l'extension",
"useRemoteSigner": "Utiliser un signataire distant",
"tryAgain": "Réessayer",
"cancel": "Annuler",
"openSignerApp": "Ouvrir l'application de signature",
"enterBunkerManually": "Saisir l'URI bunker manuellement",
"bunkerPlaceholder": "bunker://…",
"invalidBunkerFormat": "Format d'URI bunker invalide.",
"connecting": "Connexion…",
"connect": "Connecter",
"waitingForSigner": "En attente de la connexion du signataire…",
"gettingPublicKey": "Récupération de la clé publique…",
"errorEnterSecretKey": "Saisissez votre clé secrète.",
"errorInvalidSecretKey": "Clé secrète invalide. Elle doit commencer par nsec1.",
"errorLoginFailed": "Impossible de se connecter avec cette clé.",
"errorFileNoKey": "Le fichier ne contient pas de clé secrète valide.",
"errorFileRead": "Échec de la lecture du fichier.",
"errorExtensionFailed": "Échec de la connexion par extension.",
"errorBunkerConnect": "Échec de la connexion. Vérifiez l'URI bunker.",
"extensionErrorTitle": "Échec de la connexion par extension"
}, },
"onboarding": { "onboarding": {
"ariaLabel": "Configuration du compte", "ariaLabel": "Configuration du compte",
+28 -1
View File
@@ -131,7 +131,34 @@
"logout": "लॉग आउट करें", "logout": "लॉग आउट करें",
"addAccount": "एक और अकाउंट जोड़ें", "addAccount": "एक और अकाउंट जोड़ें",
"createNewAccount": "नया खाता बनाएँ", "createNewAccount": "नया खाता बनाएँ",
"loginExisting": "मौजूदा खाते से लॉग इन करें" "loginExisting": "मौजूदा खाते से लॉग इन करें",
"loginTitle": "लॉग इन करें",
"connectSignerTitle": "साइनर कनेक्ट करें",
"back": "वापस",
"secretKeyLabel": "गुप्त कुंजी का उपयोग करें",
"nsecPlaceholder": "nsec1…",
"uploadKeyFile": "कुंजी फ़ाइल अपलोड करें",
"loggingIn": "लॉग इन हो रहा है…",
"loginWithExtension": "एक्सटेंशन से लॉग इन करें",
"useRemoteSigner": "रिमोट साइनर का उपयोग करें",
"tryAgain": "पुनः प्रयास करें",
"cancel": "रद्द करें",
"openSignerApp": "साइनर ऐप खोलें",
"enterBunkerManually": "बंकर URI मैन्युअल रूप से दर्ज करें",
"bunkerPlaceholder": "bunker://…",
"invalidBunkerFormat": "अमान्य बंकर URI प्रारूप।",
"connecting": "कनेक्ट हो रहा है…",
"connect": "कनेक्ट करें",
"waitingForSigner": "साइनर कनेक्शन की प्रतीक्षा हो रही है…",
"gettingPublicKey": "सार्वजनिक कुंजी प्राप्त हो रही है…",
"errorEnterSecretKey": "अपनी गुप्त कुंजी दर्ज करें।",
"errorInvalidSecretKey": "अमान्य गुप्त कुंजी। इसे nsec1 से शुरू होना चाहिए।",
"errorLoginFailed": "इस कुंजी से लॉग इन नहीं हो सका।",
"errorFileNoKey": "फ़ाइल में मान्य गुप्त कुंजी नहीं है।",
"errorFileRead": "फ़ाइल पढ़ने में विफल।",
"errorExtensionFailed": "एक्सटेंशन लॉगिन विफल।",
"errorBunkerConnect": "कनेक्ट करने में विफल। बंकर URI जाँचें।",
"extensionErrorTitle": "एक्सटेंशन लॉगिन विफल"
}, },
"onboarding": { "onboarding": {
"ariaLabel": "अकाउंट सेटअप", "ariaLabel": "अकाउंट सेटअप",
+28 -1
View File
@@ -131,7 +131,34 @@
"logout": "Keluar", "logout": "Keluar",
"addAccount": "Tambah akun lain", "addAccount": "Tambah akun lain",
"createNewAccount": "Buat akun baru", "createNewAccount": "Buat akun baru",
"loginExisting": "Masuk dengan akun yang ada" "loginExisting": "Masuk dengan akun yang ada",
"loginTitle": "Masuk",
"connectSignerTitle": "Hubungkan penanda tangan",
"back": "Kembali",
"secretKeyLabel": "Gunakan kunci rahasia",
"nsecPlaceholder": "nsec1…",
"uploadKeyFile": "Unggah berkas kunci",
"loggingIn": "Sedang masuk…",
"loginWithExtension": "Masuk dengan ekstensi",
"useRemoteSigner": "Gunakan penanda tangan jarak jauh",
"tryAgain": "Coba lagi",
"cancel": "Batal",
"openSignerApp": "Buka aplikasi penanda tangan",
"enterBunkerManually": "Masukkan URI bunker secara manual",
"bunkerPlaceholder": "bunker://…",
"invalidBunkerFormat": "Format URI bunker tidak valid.",
"connecting": "Menghubungkan…",
"connect": "Hubungkan",
"waitingForSigner": "Menunggu koneksi penanda tangan…",
"gettingPublicKey": "Mengambil kunci publik…",
"errorEnterSecretKey": "Masukkan kunci rahasia Anda.",
"errorInvalidSecretKey": "Kunci rahasia tidak valid. Harus diawali dengan nsec1.",
"errorLoginFailed": "Tidak dapat masuk dengan kunci ini.",
"errorFileNoKey": "Berkas tidak berisi kunci rahasia yang valid.",
"errorFileRead": "Gagal membaca berkas.",
"errorExtensionFailed": "Login ekstensi gagal.",
"errorBunkerConnect": "Gagal menghubungkan. Periksa URI bunker.",
"extensionErrorTitle": "Login ekstensi gagal"
}, },
"onboarding": { "onboarding": {
"ariaLabel": "Penyiapan akun", "ariaLabel": "Penyiapan akun",
+28 -1
View File
@@ -131,7 +131,34 @@
"logout": "ចេញពីគណនី", "logout": "ចេញពីគណនី",
"addAccount": "បន្ថែមគណនីផ្សេងទៀត", "addAccount": "បន្ថែមគណនីផ្សេងទៀត",
"createNewAccount": "បង្កើតគណនីថ្មី", "createNewAccount": "បង្កើតគណនីថ្មី",
"loginExisting": "ចូលដោយគណនីដែលមានស្រាប់" "loginExisting": "ចូលដោយគណនីដែលមានស្រាប់",
"loginTitle": "ចូល",
"connectSignerTitle": "ភ្ជាប់កម្មវិធីចុះហត្ថលេខា",
"back": "ត្រឡប់",
"secretKeyLabel": "ប្រើសោសម្ងាត់",
"nsecPlaceholder": "nsec1…",
"uploadKeyFile": "ផ្ទុកឯកសារសោឡើង",
"loggingIn": "កំពុងចូល…",
"loginWithExtension": "ចូលដោយផ្នែកបន្ថែម",
"useRemoteSigner": "ប្រើកម្មវិធីចុះហត្ថលេខាពីចម្ងាយ",
"tryAgain": "ព្យាយាមម្តងទៀត",
"cancel": "បោះបង់",
"openSignerApp": "បើកកម្មវិធីចុះហត្ថលេខា",
"enterBunkerManually": "បញ្ចូល URI bunker ដោយដៃ",
"bunkerPlaceholder": "bunker://…",
"invalidBunkerFormat": "ទម្រង់ URI bunker មិនត្រឹមត្រូវ។",
"connecting": "កំពុងភ្ជាប់…",
"connect": "ភ្ជាប់",
"waitingForSigner": "កំពុងរង់ចាំការភ្ជាប់កម្មវិធីចុះហត្ថលេខា…",
"gettingPublicKey": "កំពុងទាញយកសោសាធារណៈ…",
"errorEnterSecretKey": "បញ្ចូលសោសម្ងាត់របស់អ្នក។",
"errorInvalidSecretKey": "សោសម្ងាត់មិនត្រឹមត្រូវ។ ត្រូវចាប់ផ្តើមដោយ nsec1។",
"errorLoginFailed": "មិនអាចចូលដោយសោនេះបានទេ។",
"errorFileNoKey": "ឯកសារមិនមានសោសម្ងាត់ត្រឹមត្រូវទេ។",
"errorFileRead": "បរាជ័យក្នុងការអានឯកសារ។",
"errorExtensionFailed": "ការចូលដោយផ្នែកបន្ថែមបានបរាជ័យ។",
"errorBunkerConnect": "បរាជ័យក្នុងការភ្ជាប់។ ពិនិត្យ URI bunker។",
"extensionErrorTitle": "ការចូលដោយផ្នែកបន្ថែមបានបរាជ័យ"
}, },
"onboarding": { "onboarding": {
"ariaLabel": "ការរៀបចំគណនី", "ariaLabel": "ការរៀបចំគណនី",
+28 -1
View File
@@ -131,7 +131,34 @@
"logout": "وتل", "logout": "وتل",
"addAccount": "بل حساب اضافه کول", "addAccount": "بل حساب اضافه کول",
"createNewAccount": "نوی حساب جوړ کړئ", "createNewAccount": "نوی حساب جوړ کړئ",
"loginExisting": "د موجوده حساب سره ننوتل" "loginExisting": "د موجوده حساب سره ننوتل",
"loginTitle": "ننوتل",
"connectSignerTitle": "لاسلیک کوونکی نښلول",
"back": "شاته",
"secretKeyLabel": "پټ کیلي وکاروئ",
"nsecPlaceholder": "nsec1…",
"uploadKeyFile": "د کیلي فایل پورته کول",
"loggingIn": "ننوتل کیږي…",
"loginWithExtension": "د توسیع په مرسته ننوتل",
"useRemoteSigner": "لیرې لاسلیک کوونکی وکاروئ",
"tryAgain": "بیا هڅه وکړئ",
"cancel": "لغوه کول",
"openSignerApp": "د لاسلیک کوونکي اپلیکیشن پرانیستل",
"enterBunkerManually": "د bunker URI په لاس داخل کړئ",
"bunkerPlaceholder": "bunker://…",
"invalidBunkerFormat": "د bunker URI ناسم بڼه.",
"connecting": "نښلول کیږي…",
"connect": "نښلول",
"waitingForSigner": "د لاسلیک کوونکي د نښلون په تمه…",
"gettingPublicKey": "عامه کیلي ترلاسه کیږي…",
"errorEnterSecretKey": "خپله پټه کیلي داخل کړئ.",
"errorInvalidSecretKey": "ناسمه پټه کیلي. باید په nsec1 پیل شي.",
"errorLoginFailed": "د دې کیلي سره ننوتل ونشول.",
"errorFileNoKey": "فایل کې سمه پټه کیلي نشته.",
"errorFileRead": "د فایل لوستل پاتې راغلل.",
"errorExtensionFailed": "د توسیع ننوتل پاتې راغلل.",
"errorBunkerConnect": "نښلول پاتې راغلل. د bunker URI وګورئ.",
"extensionErrorTitle": "د توسیع ننوتل پاتې راغلل"
}, },
"onboarding": { "onboarding": {
"ariaLabel": "د حساب جوړونه", "ariaLabel": "د حساب جوړونه",
+28 -1
View File
@@ -131,7 +131,34 @@
"logout": "Sair", "logout": "Sair",
"addAccount": "Adicionar outra conta", "addAccount": "Adicionar outra conta",
"createNewAccount": "Criar nova conta", "createNewAccount": "Criar nova conta",
"loginExisting": "Entrar com uma conta existente" "loginExisting": "Entrar com uma conta existente",
"loginTitle": "Entrar",
"connectSignerTitle": "Conectar assinador",
"back": "Voltar",
"secretKeyLabel": "Usar chave secreta",
"nsecPlaceholder": "nsec1…",
"uploadKeyFile": "Enviar arquivo de chave",
"loggingIn": "Entrando…",
"loginWithExtension": "Entrar com extensão",
"useRemoteSigner": "Usar um assinador remoto",
"tryAgain": "Tentar novamente",
"cancel": "Cancelar",
"openSignerApp": "Abrir aplicativo assinador",
"enterBunkerManually": "Inserir URI bunker manualmente",
"bunkerPlaceholder": "bunker://…",
"invalidBunkerFormat": "Formato de URI bunker inválido.",
"connecting": "Conectando…",
"connect": "Conectar",
"waitingForSigner": "Aguardando conexão do assinador…",
"gettingPublicKey": "Obtendo chave pública…",
"errorEnterSecretKey": "Insira sua chave secreta.",
"errorInvalidSecretKey": "Chave secreta inválida. Deve começar com nsec1.",
"errorLoginFailed": "Não foi possível entrar com esta chave.",
"errorFileNoKey": "O arquivo não contém uma chave secreta válida.",
"errorFileRead": "Falha ao ler o arquivo.",
"errorExtensionFailed": "Falha no login com extensão.",
"errorBunkerConnect": "Falha ao conectar. Verifique o URI bunker.",
"extensionErrorTitle": "Falha no login com extensão"
}, },
"onboarding": { "onboarding": {
"ariaLabel": "Configuração da conta", "ariaLabel": "Configuração da conta",
+28 -1
View File
@@ -131,7 +131,34 @@
"logout": "Выйти", "logout": "Выйти",
"addAccount": "Добавить ещё один аккаунт", "addAccount": "Добавить ещё один аккаунт",
"createNewAccount": "Создать новый аккаунт", "createNewAccount": "Создать новый аккаунт",
"loginExisting": "Войти в существующий" "loginExisting": "Войти в существующий",
"loginTitle": "Войти",
"connectSignerTitle": "Подключить подписывающее устройство",
"back": "Назад",
"secretKeyLabel": "Использовать секретный ключ",
"nsecPlaceholder": "nsec1…",
"uploadKeyFile": "Загрузить файл ключа",
"loggingIn": "Вход…",
"loginWithExtension": "Войти через расширение",
"useRemoteSigner": "Использовать удалённое подписывающее устройство",
"tryAgain": "Попробовать снова",
"cancel": "Отмена",
"openSignerApp": "Открыть приложение подписи",
"enterBunkerManually": "Ввести URI bunker вручную",
"bunkerPlaceholder": "bunker://…",
"invalidBunkerFormat": "Неверный формат URI bunker.",
"connecting": "Подключение…",
"connect": "Подключить",
"waitingForSigner": "Ожидание подключения подписывающего устройства…",
"gettingPublicKey": "Получение открытого ключа…",
"errorEnterSecretKey": "Введите ваш секретный ключ.",
"errorInvalidSecretKey": "Неверный секретный ключ. Должен начинаться с nsec1.",
"errorLoginFailed": "Не удалось войти с этим ключом.",
"errorFileNoKey": "Файл не содержит действительного секретного ключа.",
"errorFileRead": "Не удалось прочитать файл.",
"errorExtensionFailed": "Не удалось войти через расширение.",
"errorBunkerConnect": "Не удалось подключиться. Проверьте URI bunker.",
"extensionErrorTitle": "Не удалось войти через расширение"
}, },
"onboarding": { "onboarding": {
"ariaLabel": "Настройка аккаунта", "ariaLabel": "Настройка аккаунта",
+28 -1
View File
@@ -131,7 +131,34 @@
"logout": "Buda", "logout": "Buda",
"addAccount": "Wedzera imwe akaunti", "addAccount": "Wedzera imwe akaunti",
"createNewAccount": "Gadzira account itsva", "createNewAccount": "Gadzira account itsva",
"loginExisting": "Pinda neyiripo" "loginExisting": "Pinda neyiripo",
"loginTitle": "Pinda",
"connectSignerTitle": "Batanidza chisaini",
"back": "Dzokera",
"secretKeyLabel": "Shandisa kiyi yakavanzika",
"nsecPlaceholder": "nsec1…",
"uploadKeyFile": "Tumira faira rekiyi",
"loggingIn": "Kupinda…",
"loginWithExtension": "Pinda neextension",
"useRemoteSigner": "Shandisa chisaini chiri kure",
"tryAgain": "Edza zvakare",
"cancel": "Kanzura",
"openSignerApp": "Vhura app yechisaini",
"enterBunkerManually": "Isa URI ye bunker pachako",
"bunkerPlaceholder": "bunker://…",
"invalidBunkerFormat": "Fomati ye URI ye bunker isina kunaka.",
"connecting": "Kubatanidza…",
"connect": "Batanidza",
"waitingForSigner": "Kumirira kubatana kwechisaini…",
"gettingPublicKey": "Kutora kiyi yeruzhinji…",
"errorEnterSecretKey": "Isa kiyi yako yakavanzika.",
"errorInvalidSecretKey": "Kiyi yakavanzika isina kunaka. Inofanira kutanga ne nsec1.",
"errorLoginFailed": "Hazvina kukwanisa kupinda nekiyi iyi.",
"errorFileNoKey": "Faira harina kiyi yakavanzika iri nani.",
"errorFileRead": "Hazvina kukwanisa kuverenga faira.",
"errorExtensionFailed": "Kupinda neextension kwakundikana.",
"errorBunkerConnect": "Hazvina kukwanisa kubatanidza. Ongorora URI ye bunker.",
"extensionErrorTitle": "Kupinda neextension kwakundikana"
}, },
"onboarding": { "onboarding": {
"ariaLabel": "Kugadzira akaunti", "ariaLabel": "Kugadzira akaunti",
+28 -1
View File
@@ -131,7 +131,34 @@
"logout": "Toka", "logout": "Toka",
"addAccount": "Ongeza akaunti nyingine", "addAccount": "Ongeza akaunti nyingine",
"createNewAccount": "Fungua akaunti mpya", "createNewAccount": "Fungua akaunti mpya",
"loginExisting": "Ingia kwa iliyopo" "loginExisting": "Ingia kwa iliyopo",
"loginTitle": "Ingia",
"connectSignerTitle": "Unganisha kifaa cha kusaini",
"back": "Rudi",
"secretKeyLabel": "Tumia ufunguo wa siri",
"nsecPlaceholder": "nsec1…",
"uploadKeyFile": "Pakia faili ya ufunguo",
"loggingIn": "Inaingia…",
"loginWithExtension": "Ingia kwa kiendelezi",
"useRemoteSigner": "Tumia kifaa cha kusaini cha mbali",
"tryAgain": "Jaribu tena",
"cancel": "Ghairi",
"openSignerApp": "Fungua programu ya kusaini",
"enterBunkerManually": "Weka URI ya bunker mwenyewe",
"bunkerPlaceholder": "bunker://…",
"invalidBunkerFormat": "Muundo wa URI ya bunker si sahihi.",
"connecting": "Inaunganisha…",
"connect": "Unganisha",
"waitingForSigner": "Inasubiri muunganisho wa kifaa cha kusaini…",
"gettingPublicKey": "Inapata ufunguo wa umma…",
"errorEnterSecretKey": "Weka ufunguo wako wa siri.",
"errorInvalidSecretKey": "Ufunguo wa siri si sahihi. Lazima uanze na nsec1.",
"errorLoginFailed": "Imeshindwa kuingia kwa ufunguo huu.",
"errorFileNoKey": "Faili haina ufunguo wa siri halali.",
"errorFileRead": "Imeshindwa kusoma faili.",
"errorExtensionFailed": "Kuingia kwa kiendelezi kumeshindwa.",
"errorBunkerConnect": "Imeshindwa kuunganisha. Angalia URI ya bunker.",
"extensionErrorTitle": "Kuingia kwa kiendelezi kumeshindwa"
}, },
"onboarding": { "onboarding": {
"ariaLabel": "Usanidi wa akaunti", "ariaLabel": "Usanidi wa akaunti",
+28 -1
View File
@@ -131,7 +131,34 @@
"logout": "Çıkış yap", "logout": "Çıkış yap",
"addAccount": "Başka hesap ekle", "addAccount": "Başka hesap ekle",
"createNewAccount": "Yeni hesap oluştur", "createNewAccount": "Yeni hesap oluştur",
"loginExisting": "Mevcut hesapla giriş yap" "loginExisting": "Mevcut hesapla giriş yap",
"loginTitle": "Giriş yap",
"connectSignerTitle": "İmzalayıcıya bağlan",
"back": "Geri",
"secretKeyLabel": "Gizli anahtar kullan",
"nsecPlaceholder": "nsec1…",
"uploadKeyFile": "Anahtar dosyası yükle",
"loggingIn": "Giriş yapılıyor…",
"loginWithExtension": "Eklenti ile giriş yap",
"useRemoteSigner": "Uzak imzalayıcı kullan",
"tryAgain": "Tekrar dene",
"cancel": "İptal",
"openSignerApp": "İmzalayıcı uygulamasını aç",
"enterBunkerManually": "Bunker URI'sini elle gir",
"bunkerPlaceholder": "bunker://…",
"invalidBunkerFormat": "Geçersiz bunker URI biçimi.",
"connecting": "Bağlanılıyor…",
"connect": "Bağlan",
"waitingForSigner": "İmzalayıcı bağlantısı bekleniyor…",
"gettingPublicKey": "Açık anahtar alınıyor…",
"errorEnterSecretKey": "Gizli anahtarınızı girin.",
"errorInvalidSecretKey": "Geçersiz gizli anahtar. nsec1 ile başlamalı.",
"errorLoginFailed": "Bu anahtarla giriş yapılamadı.",
"errorFileNoKey": "Dosya geçerli bir gizli anahtar içermiyor.",
"errorFileRead": "Dosya okunamadı.",
"errorExtensionFailed": "Eklenti ile giriş başarısız oldu.",
"errorBunkerConnect": "Bağlanılamadı. Bunker URI'sini kontrol edin.",
"extensionErrorTitle": "Eklenti ile giriş başarısız oldu"
}, },
"onboarding": { "onboarding": {
"ariaLabel": "Hesap kurulumu", "ariaLabel": "Hesap kurulumu",
+28 -1
View File
@@ -131,7 +131,34 @@
"logout": "退出登入", "logout": "退出登入",
"addAccount": "新增其他賬戶", "addAccount": "新增其他賬戶",
"createNewAccount": "建立新帳戶", "createNewAccount": "建立新帳戶",
"loginExisting": "使用現有帳戶登入" "loginExisting": "使用現有帳戶登入",
"loginTitle": "登入",
"connectSignerTitle": "連接簽名器",
"back": "返回",
"secretKeyLabel": "使用私鑰",
"nsecPlaceholder": "nsec1…",
"uploadKeyFile": "上傳金鑰檔案",
"loggingIn": "正在登入…",
"loginWithExtension": "使用擴充功能登入",
"useRemoteSigner": "使用遠端簽名器",
"tryAgain": "重試",
"cancel": "取消",
"openSignerApp": "開啟簽名器應用程式",
"enterBunkerManually": "手動輸入 bunker URI",
"bunkerPlaceholder": "bunker://…",
"invalidBunkerFormat": "bunker URI 格式無效。",
"connecting": "正在連接…",
"connect": "連接",
"waitingForSigner": "正在等待簽名器連接…",
"gettingPublicKey": "正在取得公鑰…",
"errorEnterSecretKey": "請輸入您的私鑰。",
"errorInvalidSecretKey": "私鑰無效。必須以 nsec1 開頭。",
"errorLoginFailed": "無法使用此金鑰登入。",
"errorFileNoKey": "檔案不包含有效的私鑰。",
"errorFileRead": "讀取檔案失敗。",
"errorExtensionFailed": "擴充功能登入失敗。",
"errorBunkerConnect": "連接失敗。請檢查 bunker URI。",
"extensionErrorTitle": "擴充功能登入失敗"
}, },
"onboarding": { "onboarding": {
"ariaLabel": "帳號設定", "ariaLabel": "帳號設定",
+28 -1
View File
@@ -131,7 +131,34 @@
"logout": "退出登录", "logout": "退出登录",
"addAccount": "添加其他账户", "addAccount": "添加其他账户",
"createNewAccount": "创建新账户", "createNewAccount": "创建新账户",
"loginExisting": "使用现有账户登录" "loginExisting": "使用现有账户登录",
"loginTitle": "登录",
"connectSignerTitle": "连接签名器",
"back": "返回",
"secretKeyLabel": "使用私钥",
"nsecPlaceholder": "nsec1…",
"uploadKeyFile": "上传密钥文件",
"loggingIn": "正在登录…",
"loginWithExtension": "使用扩展登录",
"useRemoteSigner": "使用远程签名器",
"tryAgain": "重试",
"cancel": "取消",
"openSignerApp": "打开签名器应用",
"enterBunkerManually": "手动输入 bunker URI",
"bunkerPlaceholder": "bunker://…",
"invalidBunkerFormat": "bunker URI 格式无效。",
"connecting": "正在连接…",
"connect": "连接",
"waitingForSigner": "正在等待签名器连接…",
"gettingPublicKey": "正在获取公钥…",
"errorEnterSecretKey": "请输入您的私钥。",
"errorInvalidSecretKey": "私钥无效。必须以 nsec1 开头。",
"errorLoginFailed": "无法使用此密钥登录。",
"errorFileNoKey": "文件不包含有效的私钥。",
"errorFileRead": "读取文件失败。",
"errorExtensionFailed": "扩展登录失败。",
"errorBunkerConnect": "连接失败。请检查 bunker URI。",
"extensionErrorTitle": "扩展登录失败"
}, },
"onboarding": { "onboarding": {
"ariaLabel": "账户设置", "ariaLabel": "账户设置",