wacrm/messages/es.json
Arnas Donauskas 8a8758a487
feat(i18n): scaffold next-intl with [locale] routing + extract auth/layout
Introduces an internationalization layer so wacrm can ship in multiple
languages — initially en (canonical), es, lt (untranslated mirrors of
en, ready for translators).

Foundation
- next-intl ^4.13 (Next 16 + React 19 compatible)
- src/i18n/{routing,navigation,request}.ts — locale config, locale-aware
  Link/router/redirect, server-side message loader
- next.config.ts wired with `createNextIntlPlugin`
- src/app/* moved under src/app/[locale]/ (excluding /api and globals.css)
- [locale]/layout.tsx is now the root layout: <html lang={locale}>,
  hasLocale guard, setRequestLocale, NextIntlClientProvider,
  generateStaticParams for build-time prerendering of all locales
- middleware.ts composes next-intl's createMiddleware with the existing
  Supabase auth check: intl middleware runs first to handle locale
  prefix; auth redirects retain the active locale (e.g. signed-out user
  on /es/dashboard → /es/login); intl cookies (NEXT_LOCALE) are carried
  onto auth-issued responses so the sticky preference survives

Strings extracted (~80 keys across en/es/lt.json)
- common, nav (sidebar + header), roles (Owner/Admin/Agent/Viewer chips)
- auth.login, auth.signup, auth.forgotPassword (all forms + success states)
- join (all four UI states: loading, fail, accept, sign-up CTA + conflict modal)
- localeSwitcher

UI
- New `<LocaleSwitcher />` in the header — globe icon + locale dropdown
  using next-intl's useLocale + locale-aware useRouter

Out of scope for this PR (intentional — keeps diff reviewable)
- Dashboard feature pages (dashboard, inbox, contacts, pipelines,
  broadcasts, automations, flows, settings) and their components/
  namespaces still have hardcoded English; they continue to render
  fine because next/link redirects through the locale middleware
- Shared UI a11y strings (dialog close, etc.)
- Translating es.json / lt.json — they currently mirror en.json so the
  app runs in any locale without crashes (English fallback)
- Renaming middleware.ts → proxy.ts per Next 16 deprecation

Verification
- npm run build → 62 static pages prerendered across en/es/lt
- npm run typecheck → clean
- npm run test → 366 passing
- npm run lint → 0 errors (18 pre-existing warnings untouched)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-31 20:17:49 +03:00

146 lines
5.5 KiB
JSON

{
"common": {
"save": "Save",
"cancel": "Cancel",
"delete": "Delete",
"edit": "Edit",
"close": "Close",
"back": "Back",
"next": "Next",
"loading": "Loading...",
"submit": "Submit",
"retry": "Try again",
"ok": "OK"
},
"appName": "wacrm",
"appTagline": "CRM Template for WhatsApp",
"nav": {
"dashboard": "Dashboard",
"inbox": "Inbox",
"contacts": "Contacts",
"pipelines": "Pipelines",
"broadcasts": "Broadcasts",
"automations": "Automations",
"flows": "Flows",
"settings": "Settings",
"beta": "Beta",
"betaFeatureAria": "Beta feature",
"unreadAria": "{count, plural, =1 {# unread conversation} other {# unread conversations}}",
"openMenu": "Open menu",
"closeMenu": "Close menu",
"primaryAria": "Primary",
"openAccountMenu": "Open account menu",
"avatarAlt": "Avatar",
"userFallback": "User",
"profile": "Profile",
"signOut": "Sign out"
},
"roles": {
"owner": "Owner",
"admin": "Admin",
"agent": "Agent",
"viewer": "Viewer"
},
"auth": {
"login": {
"title": "Welcome back",
"titleInvite": "Sign in to accept",
"subtitle": "Sign in to your account",
"subtitleInvite": "Sign in and we'll take you to the invitation.",
"emailLabel": "Email",
"emailPlaceholder": "you@example.com",
"passwordLabel": "Password",
"passwordPlaceholder": "Enter your password",
"forgotPassword": "Forgot password?",
"submit": "Sign in",
"submitting": "Signing in...",
"noAccount": "Don't have an account?",
"createAccount": "Create account"
},
"signup": {
"title": "Create account",
"titleInvite": "Create account & join",
"subtitle": "Get started with CRM Template for WhatsApp",
"subtitleInvite": "Verify your email, then accept the invitation to join your team.",
"fullNameLabel": "Full name",
"fullNamePlaceholder": "John Doe",
"emailLabel": "Email",
"emailPlaceholder": "you@example.com",
"passwordLabel": "Password",
"passwordPlaceholder": "At least 6 characters",
"confirmPasswordLabel": "Confirm password",
"confirmPasswordPlaceholder": "Repeat your password",
"submit": "Create account",
"submitting": "Creating account...",
"haveAccount": "Already have an account?",
"signIn": "Sign in",
"passwordMismatch": "Passwords do not match",
"passwordTooShort": "Password must be at least 6 characters",
"checkEmailTitle": "Check your email",
"checkEmailBody": "We've sent a confirmation link to <highlight>{email}</highlight>. Please check your inbox and click the link to verify your account.",
"backToSignIn": "Back to sign in"
},
"forgotPassword": {
"title": "Reset password",
"subtitle": "Enter your email and we'll send you a reset link",
"emailLabel": "Email",
"emailPlaceholder": "you@example.com",
"submit": "Send reset link",
"submitting": "Sending...",
"checkEmailTitle": "Check your email",
"checkEmailBody": "We've sent a password reset link to <highlight>{email}</highlight>. Please check your inbox.",
"backToSignIn": "Back to sign in"
}
},
"join": {
"verifying": "Verifying invitation…",
"invitedToTitle": "You're invited to <highlight>{accountName}</highlight>",
"invitedAs": "You'll join as <role>{role}</role>. Link valid until {date, date, medium}.",
"acceptInvitation": "Accept invitation",
"accepting": "Accepting…",
"acceptFooter": "Accepting moves your login into <highlight>{accountName}</highlight>. Your empty personal account from signup will be cleaned up.",
"createAccountAndJoin": "Create account & join",
"alreadyHaveAccount": "I already have an account",
"conflictTitle": "Can't join {accountName} with this account",
"conflictBody": "To join <highlight>{accountName}</highlight>, sign out and sign up again with a different email address. The invite link stays valid as long as it hasn't expired.",
"staySignedIn": "Stay signed in",
"signOutAndRetry": "Sign out & use a different email",
"signingOut": "Signing out…",
"createNewAccountInstead": "Create a new account instead",
"signIn": "Sign in",
"tryAgain": "Try again",
"redeemConflictFallback": "You are already in another account. Sign in with a different email to join this one.",
"redeemFailedFallback": "Failed to accept invitation",
"redeemNetworkError": "Could not reach the server",
"signOutError": "Could not sign out. Try refreshing the page.",
"welcomeToast": "Welcome to the team",
"fail": {
"notFound": {
"title": "Invite not found",
"body": "This link doesn't match a valid invitation. Double-check the URL or ask the person who invited you to send a new one."
},
"used": {
"title": "Invite already used",
"body": "This invitation has already been accepted. If that wasn't you, ask the account admin to send a fresh link."
},
"expired": {
"title": "Invite expired",
"body": "This invitation has expired. Ask the account admin to send a new one — they take a few seconds to generate."
},
"serverError": {
"title": "Something went wrong",
"body": "We couldn't verify this invitation right now. Try refreshing the page in a moment."
}
}
},
"localeSwitcher": {
"label": "Language",
"ariaLabel": "Change language",
"locales": {
"en": "English",
"es": "Español",
"lt": "Lietuvių"
}
}
}