feat: whyrating - initial project from turbostarter boilerplate

This commit is contained in:
Alejandro Gutiérrez
2026-02-04 01:54:52 +01:00
commit 5cdc07cd39
1618 changed files with 338230 additions and 0 deletions

View File

@@ -0,0 +1,194 @@
"use client";
import dayjs from "dayjs";
import i18next from "i18next";
import LanguageDetector from "i18next-browser-languagedetector";
import resourcesToBackend from "i18next-resources-to-backend";
import { useEffect, useState } from "react";
import {
I18nextProvider,
initReactI18next,
useTranslation as useTranslationBase,
} from "react-i18next";
import * as z from "zod";
import { logger } from "@turbostarter/shared/logger";
import { getInitOptions, config } from "../config";
import { loadTranslation, makeZodI18nMap } from "../utils";
import type {
i18n,
FlatNamespace,
Namespace,
KeyPrefix,
TFunction,
} from "i18next";
import type { FallbackNs, UseTranslationOptions } from "react-i18next";
let client: i18n | null = null;
let iteration = 0;
const MAX_ITERATIONS = 20;
export const initializeI18nClient = async ({
locale,
defaultLocale,
ns,
}: {
locale?: string;
defaultLocale?: string;
ns?: Namespace;
} = {}) => {
if (client) {
return client;
}
const loadedLanguages = new Set<string>();
const loadedNamespaces = new Set<string>();
const i18n = i18next.createInstance();
await i18n
.use(initReactI18next)
.use(initReactI18next)
.use(
resourcesToBackend(
async (
language: (typeof config.locales)[number],
namespace: (typeof config.namespaces)[number],
callback,
) => {
const data = await loadTranslation(language, namespace);
if (!loadedLanguages.has(language)) {
loadedLanguages.add(language);
}
if (!loadedNamespaces.has(namespace)) {
loadedNamespaces.add(namespace);
}
return callback(null, data);
},
),
)
.use(LanguageDetector)
.init(
{
...getInitOptions({
locale,
defaultLocale,
ns,
}),
detection: {
order: ["htmlTag", "cookie", "navigator"],
caches: ["cookie"],
lookupCookie: config.cookie,
},
},
(err) => {
if (err) {
logger.error("Error initializing i18n client", err);
}
},
);
if (iteration >= MAX_ITERATIONS) {
logger.debug(`Max iterations reached: ${MAX_ITERATIONS}`);
client = i18n;
return client;
}
if (loadedLanguages.size === 0 || loadedNamespaces.size === 0) {
iteration++;
logger.debug(
`Keeping component from rendering if no languages or namespaces are loaded. Iteration: ${iteration}. Will stop after ${MAX_ITERATIONS} iterations.`,
);
throw new Error("No languages or namespaces loaded");
}
client = i18n;
return client;
};
export const useTranslation = <
Ns extends FlatNamespace | FlatNamespace[] | undefined = undefined,
KPrefix extends KeyPrefix<FallbackNs<Ns>> = undefined,
>(
ns?: Ns,
options?: UseTranslationOptions<KPrefix>,
) => useTranslationBase<Ns, KPrefix>(ns, options);
export const getI18n = async ({
locale,
defaultLocale,
ns,
}: {
locale?: string;
defaultLocale?: string;
ns?: Namespace;
}) => {
if (!client) {
return initializeI18nClient({ locale, defaultLocale, ns });
}
const t = client.getFixedT<Namespace>(client.language, ns);
await client.changeLanguage(locale);
dayjs.locale(locale);
z.config({
localeError: makeZodI18nMap({ t: t as TFunction }),
});
if (ns) {
await client.loadNamespaces(ns);
}
return client;
};
export const I18nProvider = ({
children,
locale,
defaultLocale,
ns,
}: {
children: React.ReactNode;
locale?: string;
defaultLocale?: string;
ns?: Namespace;
}) => {
const [i18nClient, setI18nClient] = useState<i18n | null>(client);
useEffect(() => {
void (async () => {
if (!client) {
setI18nClient(await getI18n({ locale, defaultLocale, ns }));
}
})();
}, [client]);
useEffect(() => {
if (i18nClient) {
void i18nClient.changeLanguage(locale);
}
}, [locale]);
if (!i18nClient) {
return null;
}
const t = i18nClient.getFixedT<Namespace>(i18nClient.language, ns);
z.config({
localeError: makeZodI18nMap({ t: t as TFunction }),
});
dayjs.locale(locale);
return <I18nextProvider i18n={i18nClient}>{children}</I18nextProvider>;
};
export type { ParseKeys as TranslationKey } from "i18next";

View File

@@ -0,0 +1,49 @@
import { logger } from "@turbostarter/shared/logger";
import { env } from "./env";
import type { InitOptions, Namespace } from "i18next";
export const config = {
locales: ["en", "es"],
defaultLocale: env.NEXT_PUBLIC_DEFAULT_LOCALE,
namespaces: [
"common",
"admin",
"organization",
"dashboard",
"auth",
"billing",
"marketing",
"validation",
],
cookie: "locale",
} as const;
export const getInitOptions = ({
locale,
defaultLocale,
ns,
}: {
locale?: string;
defaultLocale?: string;
ns?: Namespace;
}): InitOptions => ({
supportedLngs: config.locales,
fallbackLng: defaultLocale ?? config.defaultLocale,
lng: locale,
defaultNS: config.namespaces,
fallbackNS: config.namespaces,
ns: ns ?? config.namespaces,
preload: false,
interpolation: {
escapeValue: false,
},
missingInterpolationHandler: (text, value, options) => {
logger.debug(
`Missing interpolation value for key: ${text}`,
value,
options,
);
},
});

29
packages/i18n/src/env.ts Normal file
View File

@@ -0,0 +1,29 @@
/* eslint-disable turbo/no-undeclared-env-vars */
import { defineEnv } from "envin";
import * as z from "zod";
import { envConfig } from "@turbostarter/shared/constants";
import { Locale } from "./types";
import type { Preset } from "envin/types";
export const preset = {
id: "i18n",
clientPrefix: "NEXT_PUBLIC_",
client: {
NEXT_PUBLIC_DEFAULT_LOCALE: z.string().optional().default(Locale.EN),
},
server: {
DEFAULT_LOCALE: z.string().optional(),
},
} as const satisfies Preset;
export const env = defineEnv({
...envConfig,
...preset,
env: {
DEFAULT_LOCALE: process.env.DEFAULT_LOCALE,
NEXT_PUBLIC_DEFAULT_LOCALE: process.env.NEXT_PUBLIC_DEFAULT_LOCALE,
},
});

View File

@@ -0,0 +1,8 @@
/// <reference types="./typings/i18n.d.ts" />
export * from "./config";
export * from "./client";
export * from "./types";
export * from "./utils";
export { Trans } from "react-i18next/TransWithoutContext";

View File

@@ -0,0 +1,185 @@
import { match } from "@formatjs/intl-localematcher";
import dayjs from "dayjs";
import { createInstance } from "i18next";
import resourcesToBackend from "i18next-resources-to-backend";
import Negotiator from "negotiator";
import { initReactI18next } from "react-i18next/initReactI18next";
import * as z from "zod";
import { logger } from "@turbostarter/shared/logger";
import { config, getInitOptions } from "../config";
import { env } from "../env";
import { loadTranslation, makeZodI18nMap } from "../utils";
import type { i18n, Namespace, TFunction } from "i18next";
export const initializeServerI18n = async ({
locale,
defaultLocale,
ns,
}: {
locale?: string;
defaultLocale?: string;
ns?: Namespace;
}): Promise<i18n> => {
const i18n = createInstance();
const loadedNamespaces = new Set<string>();
await new Promise((resolve) => {
void i18n
.use(
resourcesToBackend(
async (
language: (typeof config.locales)[number],
namespace: (typeof config.namespaces)[number],
callback,
) => {
const data = await loadTranslation(language, namespace);
loadedNamespaces.add(namespace);
return callback(null, data);
},
),
)
.use({
type: "3rdParty",
init: async (i18next: typeof i18n) => {
let iterations = 0;
const maxIterations = 100;
while (i18next.isInitializing) {
iterations++;
if (iterations > maxIterations) {
logger.error(
`i18next is not initialized after ${maxIterations} iterations`,
);
break;
}
await new Promise((resolve) => setTimeout(resolve, 1));
}
initReactI18next.init(i18next);
resolve(i18next);
},
})
.init(getInitOptions({ locale, defaultLocale, ns }));
});
const namespaces = ns
? typeof ns === "string"
? [ns]
: ns
: config.namespaces;
// If all namespaces are already loaded, return the i18n instance
if (loadedNamespaces.size === namespaces.length) {
return i18n;
}
// Otherwise, wait for all namespaces to be loaded
const maxWaitTimeMs = 100;
const checkIntervalMs = 5;
async function waitForNamespaces() {
const startTime = Date.now();
while (Date.now() - startTime < maxWaitTimeMs) {
const allNamespacesLoaded = namespaces.every((ns) =>
loadedNamespaces.has(ns),
);
if (allNamespacesLoaded) {
return true;
}
await new Promise((resolve) => setTimeout(resolve, checkIntervalMs));
}
return false;
}
const success = await waitForNamespaces();
if (!success) {
logger.warn(
`Not all namespaces were loaded after ${maxWaitTimeMs}ms. Initialization may be incomplete.`,
);
}
return i18n;
};
export const getLocaleFromCookies = async () => {
try {
const { cookies } = await import("next/headers");
return (await cookies()).get(config.cookie)?.value;
} catch {
return undefined;
}
};
export const getLocaleFromRequest = (request?: Request) => {
if (!request) return env.DEFAULT_LOCALE ?? config.defaultLocale;
const localeCookie = request.headers
.get("cookie")
?.split(";")
.find((cookie) => cookie.trim().startsWith(`${config.cookie}=`))
?.split("=")[1]
?.trim()
.replace(/[.,]/g, "");
if (localeCookie) {
return localeCookie;
}
const negotiatorHeaders: Record<string, string> = {};
request.headers.forEach((value: string, key: string) => {
negotiatorHeaders[key] = value;
});
const languages = new Negotiator({ headers: negotiatorHeaders }).languages();
try {
return match(
languages,
config.locales,
env.DEFAULT_LOCALE ?? config.defaultLocale,
);
} catch {
return env.DEFAULT_LOCALE ?? config.defaultLocale;
}
};
export const getTranslation = async <T extends Namespace>({
locale: passedLocale,
request,
ns,
}: { locale?: string; request?: Request; ns?: T } = {}) => {
const locale =
passedLocale ??
(request ? getLocaleFromRequest(request) : null) ??
(await getLocaleFromCookies()) ??
undefined;
const i18nextInstance = await initializeServerI18n({ locale, ns });
dayjs.locale(i18nextInstance.language);
const t = i18nextInstance.getFixedT<T>(
i18nextInstance.language,
ns,
) as TFunction<T>;
z.config({
localeError: makeZodI18nMap({ t: t as TFunction }),
});
return {
t,
i18n: i18nextInstance,
};
};

View File

@@ -0,0 +1,26 @@
import dayjs from "dayjs";
import * as z from "zod";
import { env } from "../env";
import { makeZodI18nMap } from "../utils";
import { getLocaleFromCookies, initializeServerI18n } from ".";
type LayoutOrPageComponent<Params> = React.ComponentType<Params>;
export function withI18n<Params extends object>(
Component: LayoutOrPageComponent<Params>,
) {
return async function I18nServerComponentWrapper(params: Params) {
const i18n = await initializeServerI18n({
locale: await getLocaleFromCookies(),
defaultLocale: env.DEFAULT_LOCALE,
});
dayjs.locale(i18n.language);
z.config({
localeError: makeZodI18nMap({ t: i18n.t }),
});
return <Component {...params} />;
};
}

View File

@@ -0,0 +1,111 @@
{
"home": {
"header": {
"title": "Super Admin",
"description": "See your SaaS at glance and manage all your resources in one place."
},
"summary": {
"users": "The number of personal accounts that have been created.",
"organizations": "The number of organizations that have been created.",
"customers": "The number of customers for your SaaS."
}
},
"users": {
"header": {
"title": "Users",
"description": "Manage your users and their roles."
},
"user": {
"details": {
"role": {
"update": { "success": "User's role updated successfully!" }
},
"name": {
"update": { "success": "User's name updated successfully!" }
}
},
"ban": {
"description": "Ban this user from the platform. You can specify a reason and an optional expiration date for the ban.",
"success": "User banned successfully!",
"form": {
"reason": { "info": "Enter the reason for banning the user." },
"expiresIn": {
"info": "Enter the expiration date for the ban or leave it blank for no expiration."
}
}
},
"unban": { "success": "User unbanned successfully!" },
"delete": {
"title": "Delete {{name}}",
"disclaimer": "You are about to delete this user. \n\nAfter deleting it, all user data (including sessions and permissions) will be permanently deleted and cannot be recovered. \n\nDo you want to continue?",
"success": "User deleted successfully!"
},
"accounts": {
"delete": { "success": "Account deleted successfully!" },
"password": {
"update": {
"title": "Update {{name}}'s password",
"description": "Make sure to choose a strong password that meets the security requirements. This action will immediately update the user's login credentials.",
"success": "Account password updated successfully!",
"info": "Enter the new password for the account."
}
}
},
"sessions": {
"revokeAll": { "success": "All sessions revoked successfully!" }
}
}
},
"organizations": {
"header": {
"title": "Organizations",
"description": "Manage your organizations and their members."
},
"organization": {
"delete": {
"title": "Delete {{name}}",
"disclaimer": "You are about to delete this organization. \n\nAfter deleting it, all organization data (including members and teams) will be permanently deleted and cannot be recovered. \n\nDo you want to continue?",
"success": "Organization deleted successfully!"
},
"details": {
"name": {
"update": { "success": "Organization name updated successfully!" }
},
"slug": {
"update": { "success": "Organization slug updated successfully!" }
}
}
}
},
"customers": {
"header": {
"title": "Customers",
"description": "Manage your customers and their subscriptions."
},
"customer": {
"delete": { "success": "Customer deleted successfully!" },
"updatePlan": {
"title": "Update {{name}}'s plan",
"description": "Pick a plan and a status for the customer. This action will immediately update the customer's plan and status.`",
"plan": {
"info": "The plan determines the features and pricing for the customer."
},
"status": {
"info": "The status determines the subscription status for the customer."
},
"success": "Customer plan updated successfully!"
}
}
},
"error": {
"cannotCreateUsers": "You cannot create users.",
"cannotListUsers": "You cannot list users.",
"cannotUpdateUsers": "You cannot update users.",
"cannotDeleteUsers": "You cannot delete users.",
"cannotListUsersSessions": "You cannot list users sessions.",
"cannotBanUsers": "You cannot ban users.",
"cannotImpersonateUsers": "You cannot impersonate users.",
"cannotRevokeUsersSessions": "You cannot revoke users sessions.",
"cannotSetUsersPassword": "You cannot set users password."
}
}

View File

@@ -0,0 +1,209 @@
{
"chat": {
"title": "Chat",
"description": "Ask questions and get responses from popular AI models with web search and deep thinking.",
"headline": "How can I help you today?",
"composer": {
"placeholder": "What do you want to know?",
"files": {
"add": "Add files",
"remove": "Remove",
"preview": "Preview",
"dropzone": {
"title": "Drop your files here",
"description": "Drop your files here to add them to the conversation"
}
}
},
"new": "Create new chat",
"command": {
"search": "Type a command or search...",
"empty": "No results found."
},
"example": {
"summarize": {
"label": "Summarize",
"prompt": "Please provide a concise summary of the following text, highlighting the key points and main ideas while maintaining the core message. Please ensure the summary is clear and well-structured: The Industrial Revolution was a period of major industrialization and innovation during the late 18th and early 19th centuries. The Industrial Revolution began in Great Britain and quickly spread throughout Western Europe and North America. This period saw the mechanization of manufacturing, development of factory systems, and significant technological innovations in areas like textile production, steam power, and transportation. It led to major social changes including urbanization, the rise of a new working class, and eventual improvements in living standards. However, it also brought challenges like poor working conditions, child labor, and environmental pollution."
},
"analyze": {
"label": "Analyze",
"prompt": "Please analyze this dataset and provide key statistical insights, trends, and patterns. Include relevant metrics, correlations, and any notable outliers: Year,GDP,Population,Life_Expectancy\n1960,543.3,179.3,69.7\n1970,1073.3,203.3,70.8\n1980,2862.5,226.5,73.7\n1990,5980.3,248.7,75.4\n2000,10284.8,281.4,76.8\n2010,14964.4,308.7,78.7\n2020,20893.7,329.5,77.3"
},
"code": {
"label": "Code",
"prompt": "Write a function that takes a list of numbers and returns the sum of all the numbers in the list. The function should be written in Python."
},
"brainstorm": {
"label": "Brainstorm",
"prompt": "Generate a list of 10 creative ideas for a new product or service. Each idea should be unique, innovative, and have a clear market opportunity. Please provide a brief explanation for each idea."
},
"surprise": {
"label": "Surprise me",
"prompt": "Please surprise me with a random fact or interesting tidbit. I'm curious about anything and everything!"
}
}
},
"agent": {
"title": "Agents",
"description": "Personalizable AI agents that operates on your knowledge base and helps with complex tasks.",
"headline": {
"title": "Agents are coming soon!",
"subtitle": "We're working hard to bring you the best AI agents experience. Stay tuned for updates!"
}
},
"image": {
"title": "Image generation",
"description": "Create visuals from text descriptions using different models like SDXL or Midjourney.",
"headline": {
"title": "Describe what you imagine 🖼️",
"subtitle": "And let's make it live."
},
"composer": {
"placeholder": "A cat shipping its startup in minutes, not hours..."
},
"history": {
"title": "History",
"description": "View your image generation history, and see how your images have evolved over time."
},
"generation": {
"new": "Create new",
"goTo": "Go to generation",
"status": {
"idle": "Idle",
"created": "Created",
"loading": "Loading...",
"success": "Completed",
"error": "Failed"
},
"empty": {
"title": "No images yet",
"description": "There are no images generated, start by describing what you want to see."
}
},
"example": {
"fox": {
"label": "Mystic Fox",
"prompt": "A silver fox with glowing eyes emerging from a misty enchanted forest at twilight, surrounded by floating lanterns"
},
"penguin": {
"label": "Dapper Penguin",
"prompt": "A penguin in a tuxedo and monocle sipping tea at an ice bar, with northern lights dancing in the background"
},
"raccoon": {
"label": "Tech Raccoon",
"prompt": "A raccoon wearing glasses and a hoodie, coding on multiple screens in a cozy treehouse office filled with snacks"
},
"elephant": {
"label": "Zen Elephant",
"prompt": "A baby elephant meditating under a giant baobab tree at sunrise, with butterflies landing on its trunk and ears"
},
"dolphin": {
"label": "Cosmic Dolphin",
"prompt": "A dolphin leaping through space, leaving a trail of stardust and nebula colors, with distant planets visible in the background"
}
}
},
"pdf": {
"title": "Chat with PDF",
"description": "Extract insights and answers by chatting directly with your PDF documents.",
"new": "Upload new PDF document",
"recent": "Recent documents",
"command": {
"search": "Type a command or search for a document...",
"empty": "No results found."
},
"upload": {
"description": "Upload a PDF file by dragging and dropping it here, from url or by selecting a file. Let's chat!",
"fromDevice": "Select file from device",
"fromUrl": {
"placeholder": "Enter PDF url...",
"cta": "Chat!"
},
"example": {
"cta": "Try with example PDF"
},
"error": {
"api": "Failed to upload the PDF file. Please try again.",
"notFound": "Could not access the PDF file. Please check the URL and try again.",
"unauthorized": "Please sign in to upload PDF files."
},
"signIn": {
"title": "Sign in required",
"description": "Create a free account or sign in to upload and chat with your PDF documents.",
"cta": "Sign in"
},
"success": "The PDF file has been uploaded successfully!",
"confirm": {
"title": "Confirm document details",
"description": "Please confirm the details of the document you are uploading."
},
"loading": {
"title": "Bear with us...",
"description": "We're processing your document, this may take a few seconds."
}
},
"preview": {
"zoom": {
"options": "Zoom options",
"fit": "Page fit",
"in": "Zoom in",
"out": "Zoom out"
},
"navigation": {
"previous": "Previous page",
"next": "Next page"
},
"toggle": "Toggle preview",
"noDocuments": "No documents found to preview, please upload a document or try again later."
},
"composer": {
"placeholder": "Ask me anything about this document...",
"empty": "Start by asking a question. You can also preview the document by clicking on the preview button."
},
"download": {
"success": "The PDF file has been downloaded!"
},
"processing": {
"indexing": "Indexing document...",
"indexingDescription": "We're analyzing your document to enable intelligent search and Q&A. This usually takes a few seconds."
},
"suggestions": {
"title": "Try asking...",
"summarize": "Summarize this document",
"keyPoints": "What are the key points?",
"explain": "Explain the main concepts",
"find": "Find specific information"
},
"followUp": {
"moreDetail": "Tell me more about this",
"clarify": "Can you clarify that?",
"continue": "What else should I know?"
},
"selection": {
"askAbout": "Ask about this"
}
},
"tts": {
"title": "Text to speech",
"description": "Convert text to natural-sounding speech with customizable AI voices.",
"composer": {
"placeholder": "Start typing here or paste any text you want to turn into lifelike speech...",
"settings": {
"voice": {
"speed": {
"description": "Controls the speed of the generated speech. Values below 1.0 will slow down the speech, while values above 1.0 will speed it up. Extreme values may affect the quality of the generated speech."
},
"stability": {
"description": "Increasing stability will make the voice more consistent between re-generations, but it can also make it sounds a bit monotone. On longer text fragments we recommend lowering this value."
},
"similarity": {
"description": "High enhancement boosts overall voice clarity and target speaker similarity. Very high values can cause artifacts, so adjusting this setting to find the optimal value is encouraged."
},
"speakerBoost": {
"description": "Boost the similarity of the synthesized speech and the voice at the cost of some generation speed."
}
}
}
}
}
}

View File

@@ -0,0 +1,367 @@
{
"account": {
"personal": "Personal Account",
"settings": {
"title": "Settings",
"header": {
"title": "Account Settings",
"description": "Manage your account settings and preferences."
},
"security": {
"title": "Security",
"description": "Manage your account security settings."
}
},
"name": {
"edit": {
"description": "Please enter your full name, or a display name you are comfortable with.",
"success": "Name updated successfully!",
"info": "Please use 32 characters at maximum."
}
},
"avatar": {
"description": "This is your avatar.\nClick on the avatar to upload a custom one from your files.",
"info": "An avatar is optional but strongly recommended.",
"update": {
"success": "Avatar updated successfully!"
},
"remove": {
"success": "Avatar removed successfully!"
},
"validation": {
"file": "Please select an image file.",
"size": "File size must be less than {{size}}.",
"type": "Only {{types}} files are accepted."
}
},
"accounts": {
"title": "Connections",
"description": "Connect your TurboStarter Account with a third-party service to use it for login.",
"connect": {
"success": "{{provider}} account connected!"
},
"disconnect": {
"cta": "Disconnect {{provider}} account",
"success": "{{provider}} account disconnected!",
"disclaimer": "You are about to remove the Login Connection for {{provider}}.\n\nAfter removing it, you won't be able to use {{provider}} to log into your Personal Account anymore.\n\nDo you want to continue?"
},
"info": "We only access basic profile information for secure sign-in.",
"connectedAt": "Connected at {{date}}"
},
"sessions": {
"title": "Active Sessions",
"description": "Manage your active sessions and currently logged in devices.",
"info": "You can revoke any session by clicking on the trash icon.",
"revoke": {
"cta": "Revoke session",
"success": "Session revoked successfully!"
},
"noSessions": "No active sessions found."
},
"delete": {
"title": "Delete Account",
"description": "Permanently remove your Personal Account and all of its contents from the TurboStarter platform. This action is not reversible, so please continue with caution.",
"cta": "Delete Account",
"disclaimer": "You are about to delete your Personal Account. \n\nAfter deleting your account, all your data will be permanently deleted and cannot be recovered. We will send you a confirmation link to verify this action. \n\nDo you want to continue?",
"email": {
"subject": "We're sorry to see you go",
"preview": "Delete your account for TurboStarter",
"body": "Click the button below to permanently delete your account.",
"or": "Or, copy and paste this link into your browser:",
"cta": "Delete account",
"disclaimer": "If you didn't request account deletion, there's nothing to worry about, you can safely ignore this email."
},
"confirmation": {
"cta": "Send confirmation link",
"success": "Confirmation link sent, check your inbox!"
}
},
"email": {
"change": {
"description": "Enter the email address you want to use to log in. This will be used for account-related notifications.",
"success": "Check your inbox to confirm your new email!",
"info": "Email must be verified before it can be used.",
"email": {
"subject": "Confirm email address change",
"preview": "Confirm changing your email address to {{newEmail}} for TurboStarter",
"body": "You are requesting to change your email address to <bold>{{newEmail}}</bold>. Click the button below to confirm the change.",
"or": "Or, copy and paste this link into your browser:",
"cta": "Confirm change email request",
"disclaimer": "If you didn't request to change your email address, there's nothing to worry about, you can safely ignore this email."
}
},
"confirm": {
"loading": "Sending email...",
"cta": "Verify email",
"email": {
"subject": "Confirm your email address",
"preview": "Confirm your email address for TurboStarter",
"body": "One step left! Click the button below to verify your email address.",
"or": "Or, copy and paste this link into your browser:",
"cta": "Confirm email address",
"disclaimer": "If you didn't sign up for an account, there's nothing to worry about, you can safely ignore this email.",
"sent": "Verification email sent! Please check your inbox."
}
}
},
"password": {
"update": {
"title": "Update password",
"description": "Change your password here. Password must contain an uppercase letter, a special character, a number and must be at least 8 characters long.",
"info": "Try using a memorable phrase with mixed characters.",
"header": {
"title": "Update password",
"description": "Enter your new password to update your account"
},
"email": {
"subject": "Reset your password",
"preview": "Reset your password for TurboStarter",
"body": "Forgot password? Click the button below to reset your password.",
"or": "Or, copy and paste this link into your browser:",
"cta": "Reset password",
"disclaimer": "If you didn't request a password reset, there's nothing to worry about, you can safely ignore this email."
},
"cta": "Update password",
"noPassword": "You have no password set. Use the <bold>forgot password</bold> flow to set up a new one.",
"success": "Password updated successfully!"
},
"forgot": {
"title": "Forgot password",
"label": "Forgot password?",
"header": {
"title": "Forgot your password?",
"description": "Please enter your email address and we'll send you a link to reset your password"
},
"success": {
"title": "Success!",
"description": "We've sent you an email with a link to reset your password."
},
"cta": "Send reset link",
"back": "Back to sign in"
},
"require": {
"title": "Reauthenticate",
"description": "This operation requires your password to continue."
}
},
"passkeys": {
"title": "Passkeys",
"description": "Use passkeys as a secure alternative to passwords.",
"info": "Passkeys use biometrics or device PINs to provide stronger security.",
"addedAt": "Added at {{date}}",
"type": {
"singleDevice": "Single device",
"multiDevice": "Multi-device"
},
"add": {
"cta": "Add passkey",
"success": "Passkey added successfully!"
},
"remove": {
"cta": "Remove passkey",
"success": "Passkey removed successfully!",
"disclaimer": "You are about to remove a Passkey from your account.\n\nAfter removing it, you won't be able to use it to log into your Personal Account anymore.\n\nDo you want to continue?"
}
},
"twoFactor": {
"title": "Two-factor Authentication",
"description": "Add an additional layer of security by requiring a code sent to your email or phone number to sign in.",
"info": "This is an optional (but recommended) feature and can be enabled or disabled at any time.",
"enable": {
"title": "Enable Two-factor Authentication",
"description": "Once two-factor is enabled you will have to provide two methods of authentication in order to sign in into your account.\n\nIn order to enable two-factor authentication you must provide your current password below.",
"success": "Two-factor authentication enabled successfully!"
},
"disable": {
"title": "Disable Two-factor Authentication",
"description": "Are you sure you want to disable two-factor authentication? Your one-time code via authenticator app (TOTP) will cease to work and your recovery codes will be invalidated.",
"success": "Two-factor authentication disabled successfully!"
},
"totp": {
"title": "Authenticator App (TOTP)",
"description": "Generate codes using an app like Google Authenticator or Okta Verify.",
"enable": {
"title": "Enable Authenticator App",
"description": "Scan the QR code below with your preferred authenticator app. Then, enter the 6 digit code that the app provides to continue. You can also copy the secret below and paste it into your app."
},
"success": "Code verified successfully!"
},
"backupCodes": {
"title": "Recovery Codes",
"description": "Security codes when you cannot access any of your other two-factor methods.",
"save": {
"title": "Recovery Codes",
"description": "Copy and store these recovery codes in case you lost your device."
}
}
}
},
"password": "Password",
"currentPassword": "Current password",
"hidePassword": "Hide password",
"showPassword": "Show password",
"newPassword": "New password",
"divider": "or continue with",
"rememberMe": "Remember me",
"logout": {
"cta": "Log out",
"confirm": "Are you sure you want to log out?"
},
"register": {
"title": "Register",
"header": {
"title": "Create an account",
"description": "Please fill in the form below to create your account"
},
"cta": "Sign up",
"alreadyHaveAccount": "Already have an account?",
"success": {
"title": "Success!",
"description": "You have successfully registered! Now verify your email to continue.",
"notification": "Success! Now verify your email!"
}
},
"login": {
"title": "Login",
"header": {
"title": "Welcome back! 👋",
"description": "Enter your data below to login to your account"
},
"magicLink": {
"label": "Magic link",
"cta": "Send me a magic link",
"success": {
"title": "Success!",
"description": "Email with a magic link has been sent to your inbox. Click it to login!"
},
"email": {
"preview": "Login to TurboStarter with a magic link",
"subject": "Your magic link 🪄",
"body": "Almost in! Click the button below to login.",
"or": "Or, copy and paste this link into your browser:",
"cta": "Login with magic link",
"disclaimer": "If you didn't try to login, there's nothing to worry about, you can safely ignore this email."
}
},
"twoFactor": {
"totp": {
"header": {
"title": "Two-factor Authentication",
"description": "Open your authenticator app to view your authentication code and enter it to sign in."
},
"cta": "Use an authenticator app"
},
"trustDevice": "Trust this device",
"backupCode": {
"header": {
"title": "Two-factor Authentication",
"description": "Enter one of your recovery codes to sign in."
},
"placeholder": "Enter recovery code",
"cta": "Use a recovery code"
}
},
"password": {
"loading": "Signing in...",
"success": "Signed in!"
},
"phone": {
"label": "Phone"
},
"anonymous": {
"cta": "Continue as Guest"
},
"passkey": {
"cta": "Login with passkey"
},
"social": "Sign in with <capitalize>{{provider}}</capitalize>",
"cta": "Sign in",
"noAccount": "Don't have an account yet?"
},
"goToLogin": "Go to login",
"banReason": "Ban reason",
"banExpiresIn": "Ban expires in",
"updateRole": "Update role",
"error": {
"cannotSetNonExistentValue": "You cannot set a non-existent value.",
"authenticationFailed": "We couldn't authenticate you. Please try logging in again.",
"noDataToUpdate": "No data to update.",
"user": {
"cannotImpersonateAdmins": "You cannot impersonate admins.",
"notFound": "We couldn't find your account. Please check your credentials.",
"alreadyExists": "An account with this email already exists.",
"infoNotFound": "We couldn't retrieve your account information.",
"emailNotFound": "No account found with this email address.",
"accountNotFound": "We couldn't find your account.",
"alreadyHasPassword": "You already have a password set. Please use the <bold>forgot password</bold> flow to reset your password.",
"cannotBanYourself": "You cannot ban yourself.",
"cannotChangeRole": "You cannot change your role.",
"banned": "You are banned.",
"cannotGetUser": "You cannot get user.",
"cannotRemoveYourself": "You cannot remove yourself.",
"alreadyExistsUseAnotherEmail": "An account with this email already exists. Please use a different email."
},
"session": {
"creation": "Unable to start your session. Please try logging in again.",
"retrieval": "Your session could not be retrieved. Please log in again.",
"expired": "Your session has expired. Please log in again to continue."
},
"account": {
"creation": "We encountered an issue creating your account. Please try again.",
"update": "We couldn't update your account. Please try again."
},
"credentials": {
"notFound": "We couldn't find your login credentials.",
"password": {
"invalid": "The password you entered is incorrect.",
"tooShort": "Your password must be at least 8 characters long.",
"tooLong": "Your password cannot exceed 100 characters.",
"compromised": "The password you entered is compromised. Please use a different password."
},
"email": {
"invalid": "Please enter a valid email address.",
"invalidFormat": "Please enter a valid email address.",
"notVerified": "Please verify your email address before continuing.",
"cannotUpdate": "This email address cannot be updated."
},
"invalidEmailOrPassword": "The email or password you entered is incorrect."
},
"social": {
"alreadyLinked": "This social account is already connected to another user.",
"providerNotFound": "This login method is not available.",
"unlinkLastAccount": "You must keep at least one login method connected to your account."
},
"token": {
"invalid": "Your authentication token is invalid. Please try logging in again.",
"idNotSupported": "This authentication method is not supported."
},
"passkey": {
"challengeNotFound": "We couldn't find your passkey challenge.",
"notAllowed": "You are not allowed to register this passkey.",
"verificationFailed": "We couldn't verify your passkey. Please try again.",
"notFound": "We couldn't find your passkey.",
"updateFailed": "We couldn't update your passkey. Please try again."
},
"anonymous": {
"cannotSignInAgain": "Anonymous users cannot sign in again."
},
"otp": {
"notEnabled": "You must enable OTP to do this.",
"expired": "Your OTP has expired. Please request a new one."
},
"totp": {
"notEnabled": "You must enable TOTP to do this."
},
"twoFactor": {
"notEnabled": "You must enable two-factor authentication to do this.",
"invalidCookie": "Your two-factor authentication cookie is invalid. Please try again."
},
"backupCodes": {
"notEnabled": "You must enable backup codes to do this."
},
"code": {
"invalid": "The code you entered is invalid.",
"tooManyAttempts": "You have made too many attempts. Please try again later."
}
}
}

View File

@@ -0,0 +1,108 @@
{
"currency": "usd",
"pricing": {
"label": "Pricing",
"title": "Simple plans for everyone",
"description": "Choose the plan that suits your needs and start your journey to success."
},
"manage": {
"plan": {
"title": "Manage plan",
"description": "Check the details of your current plan. You can change your plan or cancel your subscription at any time."
},
"billing": {
"title": "Manage billing",
"description": "Visit the Billing Portal to manage your subscription and billing. You can update your payment method, cancel your subscription, download invoices and more.",
"visitPortal": "Visit Billing Portal"
},
"description": "Manage your billing and subscription."
},
"subscribe": "Subscribe now",
"updatePlan": "Update plan",
"getLifetimeAccess": "Get lifetime access",
"interval": {
"day": "day",
"week": "week",
"month": "month",
"year": "year",
"lifetime": "lifetime"
},
"trial": {
"cta": "Start for free",
"period": "Try with {{period}}-days trial"
},
"discount": {
"specialOffer": "SPECIAL OFFER: <bold>-{{discount}} off</bold>"
},
"plan": {
"starter": {
"name": "Starter",
"description": "For small projects just getting started",
"features": {
"sync": "Seamless sync",
"basicSupport": "Basic support",
"limitedStorage": "Limited storage",
"emailNotifications": "Email notifications",
"basicReports": "Basic reports"
}
},
"premium": {
"name": "Premium",
"description": "Perfect for growing teams with advanced features",
"badge": "Bestseller",
"features": {
"advancedSync": "Advanced sync",
"prioritySupport": "Priority support",
"moreStorage": "More storage",
"teamCollaboration": "Team collaboration",
"smsNotifications": "SMS notifications",
"advancedReports": "Advanced reports"
}
},
"enterprise": {
"name": "Enterprise",
"description": "Designed for organizations and big teams",
"features": {
"unlimitedStorage": "Unlimited storage",
"customBranding": "Custom branding",
"dedicatedSupport": "Dedicated support",
"apiAccess": "API access",
"userRoles": "User roles management",
"auditLogs": "Audit logs",
"singleSignOn": "Single sign-on",
"advancedAnalytics": "Advanced analytics"
}
}
},
"error": {
"checkout": "An error occurred while checking out.",
"checkoutRetrieve": "An error occurred while retrieving the checkout session.",
"portal": "An error occurred while getting the portal URL.",
"invalidProvider": "Invalid billing provider!",
"lemonSqueezy": "Lemon Squeezy API internal error!",
"priceNotFound": "Price not found!",
"customerNotFound": "Customer not found!",
"orderNotFound": "Order not found!",
"subscriptionNotFound": "Subscription not found!",
"webhook": {
"signatureNotFound": "Webhook signature not found!",
"signatureInvalid": "Invalid webhook signature.",
"metaInvalid": "Invalid webhook meta.",
"dataInvalid": "Invalid webhook data.",
"unsupportedEvent": "Unsupported webhook event.",
"unhandledEvent": "Unhandled webhook event."
},
"customerCreation": "Customer creation failed.",
"promotionCodeRetrieve": "Could not retrieve promotion code."
},
"status": {
"active": "Active",
"canceled": "Canceled",
"incomplete": "Incomplete",
"incompleteExpired": "Incomplete expired",
"pastDue": "Past due",
"paused": "Paused",
"trialing": "Trialing",
"unpaid": "Unpaid"
}
}

View File

@@ -0,0 +1,306 @@
{
"apps": "Apps",
"freeTools": "Free Tools",
"legal": {
"label": "Legal",
"privacy": "Privacy policy",
"terms": "Terms and Conditions",
"copyright": "All rights reserved"
},
"product": "Product",
"success": "Success!",
"auth": "Auth",
"menu": "Menu",
"open": "Open",
"lastUsed": "Last used",
"code": "Code",
"security": "Security",
"from": "From",
"to": "To",
"builtWith": "Built with",
"about": "About",
"dismiss": "Dismiss",
"install": "Install",
"contactUs": "Contact us",
"remove": "Remove",
"update": "Update",
"reset": "Reset",
"resources": "Resources",
"or": "Or",
"more": "More",
"close": "Close",
"toggle": "Toggle",
"enable": "Enable",
"disable": "Disable",
"download": "Download",
"edit": "Edit",
"add": "Add",
"regenerate": "Regenerate",
"documentation": "Documentation",
"done": "Done",
"chat": "Chat",
"copy": "Copy",
"getStarted": "Get started",
"goToDashboard": "Go to dashboard",
"home": "Home",
"ai": "AI",
"aiTools": "AI Tools",
"settings": "Settings",
"billing": "Billing",
"blog": "Blog",
"support": "Support",
"feedback": "Feedback",
"dashboard": "Dashboard",
"emojai": "Emojai",
"enabled": "Enabled",
"envin": "Envin",
"extro": "Extro",
"notEnabled": "Not enabled",
"disabled": "Disabled",
"platform": "Platform",
"account": "Account",
"continue": "Continue",
"cancel": "Cancel",
"save": "Save",
"verify": "Verify",
"slug": "Slug",
"verified": "Verified",
"tryAgain": "Try again",
"checkForUpdates": "Check for updates",
"unverified": "Unverified",
"addNew": "Add new",
"addMore": "Add more",
"provider": "Provider",
"credential": "Credential",
"accounts": "Accounts",
"banned": "Banned",
"notBanned": "Not banned",
"ban": "Ban",
"unban": "Unban",
"impersonate": "Impersonate",
"impersonating": "Impersonating",
"user": "User",
"member": "Member",
"admin": "Admin",
"owner": "Owner",
"filter": "Filter",
"you": "You",
"allRoles": "All roles",
"date": "Date",
"role": "Role",
"joinedAt": "Joined at",
"createdAt": "Created at",
"expiresIn": "Expires in",
"expiresAt": "Expires at",
"expires": "Expires",
"expired": "Expired",
"review": "Review",
"resend": "Resend",
"status": "Status",
"pending": "Pending",
"anonymous": "Anonymous",
"accepted": "Accepted",
"canceled": "Canceled",
"rejected": "Rejected",
"accept": "Accept",
"reject": "Reject",
"first": "First",
"last": "Last",
"noResults": "No results",
"rowsSelected": "{{selected}} of {{total}} row(s) selected",
"pageOf": "Page {{page}} of {{total}}",
"selected": "selected",
"selectDate": "Select date",
"selectDateRange": "Select date range",
"clear": "Clear",
"slider": "Slider",
"rowsPerPage": "Rows per page",
"leave": "Leave",
"revokeAll": "Revoke all",
"sessions": "Sessions",
"view": "View",
"searchColumns": "Search columns",
"asc": "Asc",
"desc": "Desc",
"hide": "Hide",
"previous": "Previous",
"next": "Next",
"id": "ID",
"ideasGenerator": "Ideas Generator",
"image": "Image",
"connect": "Connect",
"logo": "Logo",
"disconnect": "Disconnect",
"plan": "Plan",
"plans": "Plans",
"delete": "Delete",
"invite": "Invite",
"searchPlaceholder": "Search...",
"organization": "Organization",
"members": "Members",
"memberships": "Memberships",
"invitations": "Invitations",
"actions": "Actions",
"affiliates": "Affiliates",
"agent": "Agent",
"organizations": "Organizations",
"avatar": "Avatar",
"learnMore": "Learn more",
"upgrade": "Upgrade",
"updatedAt": "Updated at",
"visitors": "Visitors",
"desktop": "Desktop",
"mobile": "Mobile",
"other": "Other",
"pdf": "PDF",
"views": "Views",
"users": "Users",
"customers": "Customers",
"lastDays": "Last {{count}} days",
"lastMonths_one": "Last month",
"lastMonths_other": "Last {{count}} months",
"selectMonth": "Select month",
"seeRoadmap": "See roadmap",
"general": "General",
"notifications": "Notifications",
"rate": "Rate",
"share": "Share",
"syncreads": "SyncReads",
"privacy": "Privacy",
"reason": "Reason",
"never": "Never",
"language": {
"label": "Language",
"description": "Pick a language for your app.",
"info": "This will change the language of the app. It will not change the language of the browser.",
"change": "Change language"
},
"theme": {
"title": "Theme",
"customization": {
"title": "Customize",
"label": "Customize theme",
"description": "Pick a style and color for your app."
},
"color": {
"label": "Color",
"orange": "Orange",
"rose": "Rose",
"red": "Red",
"yellow": "Yellow",
"gray": "Gray",
"stone": "Stone",
"green": "Green",
"blue": "Blue",
"violet": "Violet"
},
"mode": {
"label": "Mode",
"system": "System",
"light": "Light",
"dark": "Dark"
}
},
"name": "Name",
"message": "Message",
"email": "Email",
"skip": "Skip",
"finish": "Finish",
"two-factor": "Two-factor Authentication",
"error": {
"title": "Oops!",
"general": "Something went wrong, please try again.",
"apologies": "Apologies, an error occurred while processing your request. Please contact us if the issue persists.",
"invalid": "Invalid payload!",
"notFound": "Not found!",
"resourceDoesNotExist": "The resource you are looking for does not exist.",
"unauthorized": "You need to be logged in to access this feature!",
"forbidden": "You don't have permission to access this feature!",
"insufficientCredits": "Insufficient credits",
"rateLimit": "Rate limit exceeded"
},
"search": {
"label": "Search",
"inProgress": "Searching...",
"completed": "Search completed"
},
"greeting": {
"morning": "Good morning",
"afternoon": "Good afternoon",
"evening": "Good evening",
"night": "Good night"
},
"like": "Like",
"dislike": "Dislike",
"reload": "Reload",
"creditsLeft": "Credits left",
"credits": {
"description": "You have {{count}} credits remaining",
"description2": "Credits are used for AI operations",
"description3": "Upgrade your plan for more credits",
"cta": "Get more credits"
},
"rateLimit": {
"description": "You've hit your rate limit",
"description2": "Please wait before making more requests",
"description3": "Upgrade your plan for higher limits",
"cta": "Upgrade plan"
},
"goBackHome": "Go back home",
"history": "History",
"current": "Current",
"stop": "Stop",
"send": "Send",
"record": "Record",
"recording": "Recording...",
"processing": "Processing",
"transcribing": "Transcribing...",
"microphoneDenied": "Microphone access denied",
"transcriptionFailed": "Transcription failed. Please try again.",
"pressEscapeToCancel": "Press Escape to cancel",
"newTab": "Open in new tab",
"results": "Results",
"analyzingImage": "Analyzing image...",
"reasoning": {
"inProgress": "Thinking...",
"completed": "Reasoning completed"
},
"theme.toggle": "Toggle theme",
"tts": "Text to Speech",
"speed": "Speed",
"slower": "Slower",
"faster": "Faster",
"stability": "Stability",
"moreVariable": "More variable",
"moreStable": "More stable",
"similarity": "Similarity",
"low": "Low",
"high": "High",
"speakerBoost": "Speaker boost",
"pause": "Pause",
"play": "Play",
"second_one": "{{count}} second",
"second_other": "{{count}} seconds",
"square": "Square",
"standard": "Standard",
"landscape": "Landscape",
"portrait": "Portrait",
"model": "Model",
"aspectRatio": "Aspect ratio",
"count": "Count",
"completedAt": "Completed at",
"details": "Details",
"file": "File",
"size": "Size",
"type": "Type",
"confirm": "Confirm",
"downloading": "Downloading...",
"canvases": "Canvases",
"threads": "Threads",
"briefing": "Briefing",
"connections": "Connections",
"vocabulary": "Vocabulary",
"dataHealth": "Data Health",
"demos": "Demos",
"dev": "Dev"
}

View File

@@ -0,0 +1,26 @@
{
"user": {
"home": {
"title": "Dashboard",
"description": "An overview of your personal account's activity across all your projects."
}
},
"organization": {
"home": {
"title": "Organization dashboard",
"description": "An overview of your organization's activity and performance across all projects."
}
},
"chart": {
"showing": "Showing total visitors for the last period",
"trending": "Trending by up to 5.2% this month",
"period": "January - June 2024",
"bar": "Bar Chart",
"pie": "Pie Chart",
"area": "Area Chart",
"line": "Line Chart",
"shape": "Shape Chart",
"radial": "Radial Chart",
"radar": "Radar Chart"
}
}

View File

@@ -0,0 +1,11 @@
export const en = {
common: () => import("./common.json"),
auth: () => import("./auth.json"),
billing: () => import("./billing.json"),
marketing: () => import("./marketing.json"),
organization: () => import("./organization.json"),
admin: () => import("./admin.json"),
dashboard: () => import("./dashboard.json"),
validation: () => import("./validation.json"),
ai: () => import("./ai.json"),
} as const;

View File

@@ -0,0 +1,242 @@
{
"product": {
"title": "This could be your very own $1M startup",
"description": "A fully customizable TurboStarter application. Swap in your content and launch your product faster than ever.",
"mobile": {
"ios": {
"title": "iOS App",
"description": "Native experience on iPhone and iPad"
},
"android": {
"title": "Android App",
"description": "Optimized for Android phones"
}
},
"extension": {
"chrome": {
"title": "Chrome Extension",
"description": "Integration with Chrome browser"
},
"firefox": {
"title": "Firefox Add-on",
"description": "Enhanced browsing for Firefox users"
},
"edge": {
"title": "Edge Add-on",
"description": "Powerful tools for Microsoft Edge"
}
}
},
"announcement": "Your feature announcement goes here",
"shippedWith": "Shipped with production-ready tools",
"cta": {
"question": "Ready to start?",
"button": "Start for free",
"buy": {
"question": "Ready to start?",
"description": "Purchase to unlock full access to the codebase and start shipping your product today. On all platforms.",
"button": "Get access",
"join": {
"description": "Join other founders customizing TurboStarter, share launch tactics, and stay close to new drops.",
"button": "Join the community"
}
}
},
"editToReload": "Edit <code>{{file}}</code> and save to reload.",
"workInProgress": {
"title": "We're working on it!",
"description": "{{feature}} is currently not available. We're working on it and it will be available soon."
},
"features": {
"label": "Features",
"title": "Turn visitors into customers",
"description": "Everything you need to build, deploy, and scale your applications with confidence.",
"feature": {
"core": {
"title": "Core foundation",
"description": "Authentication, database, and API already wired up so you can focus on customer value."
},
"ai": {
"title": "AI-powered capabilities",
"description": "Intelligent features with smart recommendations and automated workflows powered by AI."
},
"mobile": {
"title": "Native mobile experience",
"description": "Fully-featured mobile app with seamless synchronization across all your devices."
},
"extension": {
"title": "Browser extension",
"description": "Keep users engaged in their browser with customizable shortcuts to your product."
}
}
},
"testimonials": {
"label": "Testimonials",
"title": "Teams ship faster with us",
"description": "Hear from founders and developers who customized the starter to launch sooner and convert better.",
"rating": "{{rating}} from {{count}}+ reviews",
"reviews": {
"jack": {
"name": "Jack",
"position": "Full Stack Developer",
"body": "The starter freed our team from boilerplate work. We swapped in our copy over a weekend and focused on features customers actually wanted."
},
"jill": {
"name": "Jill",
"position": "Startup Founder",
"body": "We launched our MVP in two weeks. Everything from marketing pages to auth was ready—we just rewrote the copy for our audience."
},
"john": {
"name": "John",
"position": "Senior Developer",
"body": "The DX is unmatched. The stack works out of the box and stays flexible enough to shape around our product narrative."
},
"sarah": {
"name": "Sarah",
"position": "Product Manager",
"body": "We scaled from prototype to thousands of users without drama. Analytics, billing, and marketing flows were ready to customize."
},
"mike": {
"name": "Mike",
"position": "Engineering Lead",
"body": "The community is stellar. Fast answers, launch feedback, and constant improvements keep our team moving."
}
}
},
"faq": {
"label": "FAQ",
"title": "Have a question? We got answers",
"description": "Find answers to frequently asked questions about features, pricing, implementation, and getting started with our platform.",
"cta": "Contact us",
"question": {
"whatDoesOurPlatformDo": {
"question": "What does our platform do?",
"answer": "TurboStarter gives you a full-stack SaaS foundation—marketing site, web app, mobile apps, and extensions—so you can focus on your unique customer experience."
},
"howWillThisBenefitMyBusiness": {
"question": "How will this benefit my business?",
"answer": "You launch faster, ship features sooner, and convert more leads because the core product, analytics, and billing flows are already in place."
},
"isMyDataSafe": {
"question": "Is my data safe?",
"answer": "Yes. We follow enterprise-grade security practices and provide guardrails so you can adapt policies to your industry requirements."
},
"whatKindOfIntegrationsAreAvailable": {
"question": "What kind of integrations are available?",
"answer": "TurboStarter ships with popular SaaS integrations out of the box, and the modular architecture makes adding your stack straightforward."
},
"howEasyIsItToOnboardMyTeam": {
"question": "How easy is it to onboard my team?",
"answer": "Your team can dive in quickly with clear docs, TypeScript-first patterns, and examples that show exactly where to customize."
},
"whatTypesOfBusinessesCanUseThis": {
"question": "What types of businesses can use this?",
"answer": "From solo founders to enterprise innovation teams, TurboStarter adapts to any SaaS use case that needs a head start."
},
"canICustomizeThisToFitMyBusinessNeeds": {
"question": "Can I customize this to fit my business needs?",
"answer": "Absolutely. Every module is designed for quick customization—update copy, swap components, and extend logic without fighting the framework."
}
}
},
"contact": {
"label": "Contact",
"title": "Get in touch with us today for support",
"cta": "Talk to sales",
"description": "Reach us for assistance or feedback",
"form": {
"name": {
"placeholder": "John Doe"
},
"email": {
"placeholder": "your@email.com"
},
"message": {
"placeholder": "How can we help you?"
},
"submit": "Send message",
"success": {
"title": "Message sent!",
"description": "Thank you for your message. We'll get back to you soon."
}
},
"email": {
"subject": "You've received a new contact form submission",
"body": "New contact form submission"
}
},
"blog": {
"label": "Blog",
"title": "News and updates about the platform",
"description": "Stay up to date with the latest news, insights, and updates from our platform. Discover new features, best practices, and industry trends.",
"timeToRead": "{{time}} min read",
"tag": {
"learning": "learning",
"skills": "skills",
"progress": "progress",
"product": "product",
"development": "development",
"prototype": "prototype",
"launch": "launch"
}
},
"api": {
"title": "API Reference",
"description": "Build integrations with our API"
},
"roadmap": {
"title": "Roadmap",
"description": "See what's coming next"
},
"docs": {
"title": "Docs",
"description": "Learn how to use TurboStarter"
},
"ai": {
"description": "Unlock productivity and innovation with TurboStarter's AI assistant. Get instant answers, automate tasks, and boost your workflow using advanced artificial intelligence—secure, reliable, and designed for modern teams.",
"prompt": {
"history": "Tell the history of the internet",
"capitals": "Quiz me on the world capitals",
"quantum": "Explain quantum computing",
"realWorld": "Describe a real-world AI case"
},
"placeholder": "Ask a question...",
"cta": "Submit"
},
"setup": {
"steps": {
"step": {
"start": {
"title": "This is a simple step",
"description": "You need to press Continue button to proceed to the next step"
},
"required": {
"title": "This is a required step",
"description": "You have to complete all the required fields to proceed to the next step",
"fields": {
"data": "I understand that my data is private and encrypted",
"privacy": "I've read and agree to the <a>Privacy Policy</a>"
}
},
"skip": {
"title": "This is a skip step",
"description": "You can skip this step and proceed to the next step"
},
"final": {
"title": "This is a final step",
"description": "You have completed all the steps, enjoy your new app!"
}
}
}
},
"update": {
"available": {
"title": "New update available!",
"description": "A new update is ready to be installed. Click below to install it or dismiss for later."
},
"installing": {
"title": "Installing update...",
"description": "The update is being installed. Please wait a moment."
}
}
}

View File

@@ -0,0 +1,198 @@
{
"create": {
"title": "Create organization",
"description": "Create a new organization to manage your team and projects.",
"cta": "Create organization",
"info": "Your organization name should be unique and descriptive."
},
"join": {
"title": "Join organization",
"description": "Join an organization to collaborate with your team, manage projects efficiently, and access shared resources."
},
"settings": {
"title": "Settings",
"header": {
"title": "Organization settings",
"description": "Manage your organization settings and preferences."
}
},
"invitations": {
"title": "Invitations",
"disclaimer": {
"title": "You have been invited to join an organization",
"description": "Sign in or create an account to accept the invitation and join the organization."
},
"resend": {
"success": "Invitation resent successfully!"
},
"cancel": {
"success": "Invitation canceled successfully!"
},
"accept": {
"success": "You're now a member of {{organization}}!"
},
"reject": {
"success": "You have rejected to join {{organization}}."
},
"expired": {
"title": "Invitation not found or expired",
"description": "The invitation you are looking for is either expired or does not exist. Please contact the organization owner to renew the invite.",
"cta": "Back to dashboard"
},
"emailMismatch": {
"title": "You're currently logged in with a different account",
"description": "The email you are trying to use does not match the invitation email. Please sign in as <bold>{{email}}</bold> or contact the organization owner to update the invitation.",
"cta": "Sign in as {{email}}",
"skip": "Skip to dashboard"
},
"invitation": {
"title": "Join {{organizationName}}",
"description": "<bold>{{inviterEmail}}</bold> has invited you to join <bold>{{organizationName}}</bold> on TurboStarter.",
"skip": "Skip to dashboard"
},
"user": {
"banner": {
"title_one": "You have pending invitation",
"title_other": "You have {{count}} pending invitations",
"description": "Click to review the details and accept or reject the invitation."
},
"list": {
"title": "Pending invitations",
"description": "View and manage your pending invitations."
}
}
},
"members": {
"title": "Members",
"header": {
"title": "Organization members",
"description": "Manage your organization members, invitations and roles."
},
"invite": {
"title": "Invite member",
"description": "To add a new member to your organization, send them an invitation.",
"info": "You can invite multiple members at once.",
"missingPermission": "You need additional permissions to invite members to this organization.",
"success_one": "Member invited successfully!",
"success_other": "Members invited successfully!",
"email": {
"subject": "You have been invited to join an organization",
"preview": "Join {{inviter}} on TurboStarter",
"body": "<bold>{{inviter}}</bold> has invited you to join <bold>{{organization}}</bold> on TurboStarter.",
"or": "Or, copy and paste this link into your browser:",
"cta": "Join {{organization}}",
"disclaimer": "If you were not expecting this invitation, there's nothing to worry about, you can safely ignore this email."
}
},
"update": {
"role": {
"title": "Update {{name}}'s role",
"description": "Change the role of the selected member by selecting it from the dropdown menu.",
"info": "The role determines the permissions of the member.",
"success": "Member's role updated successfully!"
}
},
"remove": {
"success": "Member removed successfully!"
}
},
"name": {
"edit": {
"description": "Please enter your organization name, or a display name you are comfortable with.",
"success": "Organization name updated successfully!",
"info": "Please use 32 characters at maximum.",
"missingPermission": "You need additional permissions to manage your organization's name."
}
},
"logo": {
"description": "This is your organization logo.\nClick on the logo to upload a custom one from your files.",
"info": "While not required, adding a logo is highly recommended.",
"missingPermission": "You need additional permissions to manage your organization's logo.",
"update": {
"success": "Organization logo updated successfully!"
},
"remove": {
"success": "Organization logo removed successfully!"
}
},
"leave": {
"title": "Leave organization",
"description": "Revoke your access to this organization. Any resources you've added to the organization will remain.",
"cta": "Leave organization",
"disclaimer": "Are you sure you want to leave this organization?\n\nYou will no longer have access to its resources and will need to be invited again to join.\n\nDo you want to continue?",
"success": "You have left the organization successfully!",
"cannotLeaveAsOnlyOwner": "To leave this organization, ensure at least one more member has the Owner role."
},
"delete": {
"title": "Delete organization",
"description": "Permanently remove this organization and all of its contents from the TurboStarter platform. This action is not reversible, so please continue with caution.",
"cta": "Delete organization",
"disclaimer": "You are about to delete this organization. \n\nAfter deleting it, all organization data (including members and teams) will be permanently deleted and cannot be recovered. \n\nDo you want to continue?",
"success": "Organization deleted successfully!",
"missingPermission": "You need additional permissions to delete this organization."
},
"error": {
"slugAlreadyTaken": "The slug is already taken.",
"cannotCreateNew": "You cannot create a new organization.",
"maximumNumberOfOrganizations": "You have reached the maximum number of organizations.",
"alreadyExists": "An organization with this name already exists.",
"notFound": "We couldn't find the organization.",
"userNotMember": "You are not a member of this organization.",
"cannotUpdate": "You cannot update this organization.",
"cannotDelete": "You cannot delete this organization.",
"noActive": "No active organization found.",
"userAlreadyMember": "You are already a member of this organization.",
"memberNotFound": "We couldn't find the member.",
"roleNotFound": "We couldn't find the role.",
"cannotLeaveAsOnlyOwner": "You cannot leave the organization as the only owner.",
"cannotLeaveWithoutOwner": "You cannot leave the organization without an owner.",
"cannotDeleteMember": "You cannot delete this member.",
"cannotUpdateMember": "You cannot update this member.",
"membershipLimitReached": "You have reached the membership limit for this organization.",
"cannotAccess": "You cannot access this organization.",
"slugNotAvailable": "The slug is not available.",
"team": {
"cannotCreateNew": "You cannot create a new team.",
"alreadyExists": "A team with this name already exists.",
"notFound": "We couldn't find the team.",
"maximumNumberOfTeams": "You have reached the maximum number of teams.",
"unableToRemoveLastTeam": "You cannot remove the last team.",
"cannotCreate": "You cannot create a team in this organization.",
"cannotDelete": "You cannot delete a team in this organization.",
"cannotUpdate": "You cannot update a team in this organization.",
"cannotAccessMembers": "You cannot access the members of this team.",
"memberLimitReached": "You have reached the member limit for this team.",
"userNotMember": "You are not a member of this team.",
"noActive": "No active team found.",
"cannotCreateNewMember": "You cannot create a new member.",
"cannotRemoveMember": "You cannot remove this member.",
"cannotAccess": "You cannot access this organization."
},
"invitation": {
"cannotInviteUsers": "You cannot invite users to this organization.",
"userAlreadyInvited": "This user is already invited to this organization.",
"notFound": "We couldn't find the invitation.",
"notRecipient": "You are not the recipient of this invitation.",
"cannotCancel": "You cannot cancel this invitation.",
"inviterNoLongerMember": "The inviter is no longer a member of the organization.",
"cannotInviteUserWithRole": "You cannot invite a user with this role.",
"emailVerificationRequired": "Email verification is required before accepting or rejecting an invitation.",
"failedToRetrieve": "We couldn't retrieve the invitation.",
"limitReached": "You have reached the invitation limit for this organization."
},
"ac": {
"missingAcInstance": "Missing AC instance.",
"mustBeInOrganizationToCreateRole": "You must be in an organization to create a role.",
"cannotCreateRole": "You cannot create a role.",
"cannotUpdateRole": "You cannot update a role.",
"cannotDeleteRole": "You cannot delete a role.",
"cannotReadRole": "You cannot read a role.",
"cannotListRole": "You cannot list roles.",
"cannotGetRole": "You cannot get a role.",
"tooManyRoles": "You have reached the maximum number of roles.",
"invalidResource": "The resource is invalid.",
"cannotDeletePreDefinedRole": "You cannot delete a pre-defined role.",
"roleNameAlreadyTaken": "The role name is already taken."
}
}
}

View File

@@ -0,0 +1,146 @@
{
"error": {
"default": "Invalid input value",
"type": "Expected {{expected}}, but received {{received}}",
"type_with_path": "{{path}} should be {{expected}}, but received {{received}}",
"undefined": "{{path}} is required",
"date": "Invalid date format",
"custom": "Invalid input value",
"notMultipleOf": "Number must be a multiple of {{multipleOf}}",
"invalidKey": "Invalid key",
"invalidElement": "Invalid element: {{- origin}}",
"invalidValue_one": "Invalid value: {{- values}}, expected {{expected}}",
"invalidValue_other": "Invalid values: {{- values}}, expected {{expected}}",
"string": {
"generic": "Invalid format - {{format}}",
"email": "Please enter a valid email address",
"url": "Please enter a valid URL",
"uuid": "Please enter a valid UUID",
"cuid": "Please enter a valid CUID",
"regex": "Invalid format - {{validation}}",
"datetime": "Invalid format - {{validation}}",
"startsWith": "Must start with {{startsWith}}",
"endsWith": "Must end with {{endsWith}}"
},
"file": {
"type": "Only {{type}} files are accepted.",
"maxCount_one": "You can only upload {{count}} file.",
"maxCount_other": "You can only upload up to {{count}} files."
},
"tooSmall": {
"array": {
"inclusive": "Must contain at least {{minimum}} element(s)",
"inclusive_one": "Must contain at least {{minimum}} element",
"inclusive_other": "Must contain at least {{minimum}} elements",
"notInclusive": "Must contain more than {{minimum}} element(s)",
"notInclusive_one": "Must contain more than {{minimum}} element",
"notInclusive_other": "Must contain more than {{minimum}} elements"
},
"string": {
"inclusive": "Must contain at least {{minimum}} character(s)",
"inclusive_one": "Must contain at least {{minimum}} character",
"inclusive_other": "Must contain at least {{minimum}} characters",
"inclusive_with_path": "{{path}} must contain at least {{minimum}} character(s)",
"inclusive_with_path_one": "{{path}} must contain at least {{minimum}} character",
"inclusive_with_path_other": "{{path}} must contain at least {{minimum}} characters",
"notInclusive": "Must contain more than {{minimum}} character(s)",
"notInclusive_one": "Must contain more than {{minimum}} character",
"notInclusive_other": "Must contain more than {{minimum}} characters",
"notInclusive_with_path": "{{path}} must contain more than {{minimum}} character(s)",
"notInclusive_with_path_one": "{{path}} must contain more than {{minimum}} character",
"notInclusive_with_path_other": "{{path}} must contain more than {{minimum}} characters"
},
"number": {
"inclusive": "Must be greater than or equal to {{minimum}}",
"inclusive_with_path": "{{path}} must be greater than or equal to {{minimum}}",
"notInclusive": "Must be greater than {{minimum}}",
"notInclusive_with_path": "{{path}} must be greater than {{minimum}}"
},
"set": {
"inclusive": "Invalid input value",
"notInclusive": "Invalid input value"
},
"date": {
"inclusive": "Date must be on or after {{- minimum, datetime}}",
"notInclusive": "Date must be after {{- minimum, datetime}}"
},
"file": {
"inclusive": "File size must be greater than or equal to {{minimum}}MB",
"notInclusive": "File size must be greater than {{minimum}}MB"
}
},
"tooBig": {
"array": {
"inclusive": "Must contain at most {{maximum}} element(s)",
"inclusive_one": "Must contain at most {{maximum}} element",
"inclusive_other": "Must contain at most {{maximum}} elements",
"notInclusive": "Must contain fewer than {{maximum}} element(s)",
"notInclusive_one": "Must contain fewer than {{maximum}} element",
"notInclusive_other": "Must contain fewer than {{maximum}} elements"
},
"string": {
"inclusive": "Must contain at most {{maximum}} character(s)",
"inclusive_one": "Must contain at most {{maximum}} character",
"inclusive_other": "Must contain at most {{maximum}} characters",
"inclusive_with_path": "{{path}} must not exceed {{maximum}} character(s)",
"inclusive_with_path_one": "{{path}} must not exceed {{maximum}} character",
"inclusive_with_path_other": "{{path}} must not exceed {{maximum}} characters",
"notInclusive": "Must contain fewer than {{maximum}} character(s)",
"notInclusive_one": "Must contain fewer than {{maximum}} character",
"notInclusive_other": "Must contain fewer than {{maximum}} characters",
"notInclusive_with_path": "{{path}} must contain fewer than {{maximum}} character(s)",
"notInclusive_with_path_one": "{{path}} must contain fewer than {{maximum}} character",
"notInclusive_with_path_other": "{{path}} must contain fewer than {{maximum}} characters"
},
"number": {
"inclusive": "Must be less than or equal to {{maximum}}",
"inclusive_with_path": "{{path}} must be less than or equal to {{maximum}}",
"notInclusive": "Must be less than {{maximum}}",
"notInclusive_with_path": "{{path}} must be less than {{maximum}}"
},
"set": {
"inclusive": "Invalid input value",
"notInclusive": "Invalid input value"
},
"date": {
"inclusive": "Date must be on or before {{- maximum, datetime}}",
"notInclusive": "Date must be before {{- maximum, datetime}}"
},
"file": {
"inclusive": "File size must be less than or equal to {{maximum}}MB",
"notInclusive": "File size must be less than {{maximum}}MB"
}
}
},
"validation": {
"email": "email",
"url": "url",
"uuid": "uuid",
"cuid": "cuid",
"regex": "regex",
"datetime": "datetime"
},
"type": {
"function": "function",
"number": "number",
"string": "string",
"nan": "nan",
"integer": "integer",
"float": "float",
"boolean": "boolean",
"date": "date",
"bigint": "bigint",
"undefined": "undefined",
"symbol": "symbol",
"null": "null",
"array": "array",
"object": "object",
"unknown": "unknown",
"promise": "promise",
"void": "void",
"never": "never",
"map": "map",
"set": "set"
}
}

View File

@@ -0,0 +1,119 @@
{
"home": {
"header": {
"title": "Super Admin",
"description": "Ve tu SaaS de un vistazo y gestiona todos tus recursos en un solo lugar."
},
"summary": {
"users": "El número de cuentas personales creadas.",
"organizations": "El número de organizaciones creadas.",
"customers": "El número de clientes de tu SaaS."
}
},
"users": {
"header": {
"title": "Usuarios",
"description": "Gestiona tus usuarios y sus roles."
},
"user": {
"details": {
"role": {
"update": { "success": "¡Rol de usuario actualizado exitosamente!" }
},
"name": {
"update": {
"success": "¡Nombre de usuario actualizado exitosamente!"
}
}
},
"ban": {
"description": "Banear a este usuario de la plataforma. Puedes especificar un motivo y una fecha de expiración opcional.",
"success": "¡Usuario baneado exitosamente!",
"form": {
"reason": { "info": "Ingresa el motivo del baneo." },
"expiresIn": {
"info": "Ingresa la fecha de expiración o deja en blanco para que no expire."
}
}
},
"unban": { "success": "¡Usuario desbaneado exitosamente!" },
"delete": {
"title": "Eliminar {{name}}",
"disclaimer": "Estás a punto de eliminar este usuario. \n\nTras eliminarlo, todos sus datos (incluidas sesiones y permisos) se eliminarán permanentemente y no podrán recuperarse. \n\n¿Deseas continuar?",
"success": "¡Usuario eliminado exitosamente!"
},
"accounts": {
"delete": { "success": "¡Cuenta eliminada exitosamente!" },
"password": {
"update": {
"title": "Actualizar la contraseña de {{name}}",
"description": "Elige una contraseña fuerte que cumpla con los requisitos de seguridad. Esta acción actualizará inmediatamente las credenciales de inicio de sesión.",
"success": "¡Contraseña de la cuenta actualizada exitosamente!",
"info": "Ingresa la nueva contraseña de la cuenta."
}
}
},
"sessions": {
"revokeAll": {
"success": "¡Todas las sesiones revocadas exitosamente!"
}
}
}
},
"organizations": {
"header": {
"title": "Organizaciones",
"description": "Gestiona tus organizaciones y sus miembros."
},
"organization": {
"delete": {
"title": "Eliminar {{name}}",
"disclaimer": "Estás a punto de eliminar esta organización. \n\nTras eliminarla, todos los datos (incluidos miembros y equipos) se eliminarán permanentemente y no podrán recuperarse. \n\n¿Deseas continuar?",
"success": "¡Organización eliminada exitosamente!"
},
"details": {
"name": {
"update": {
"success": "¡Nombre de la organización actualizado exitosamente!"
}
},
"slug": {
"update": {
"success": "¡Slug de la organización actualizado exitosamente!"
}
}
}
}
},
"customers": {
"header": {
"title": "Clientes",
"description": "Gestiona tus clientes y sus suscripciones."
},
"customer": {
"delete": { "success": "¡Cliente eliminado exitosamente!" },
"updatePlan": {
"title": "Actualizar el plan de {{name}}",
"description": "Elige un plan y un estado para el cliente. Esta acción actualizará inmediatamente el plan y el estado.",
"plan": {
"info": "El plan determina funciones y precios para el cliente."
},
"status": {
"info": "El estado determina el estado de la suscripción."
},
"success": "¡Plan del cliente actualizado exitosamente!"
}
}
},
"error": {
"cannotCreateUsers": "No puedes crear usuarios.",
"cannotListUsers": "No puedes listar usuarios.",
"cannotUpdateUsers": "No puedes actualizar usuarios.",
"cannotDeleteUsers": "No puedes eliminar usuarios.",
"cannotListUsersSessions": "No puedes listar sesiones de usuarios.",
"cannotBanUsers": "No puedes banear usuarios.",
"cannotImpersonateUsers": "No puedes suplantar usuarios.",
"cannotRevokeUsersSessions": "No puedes revocar sesiones de usuarios.",
"cannotSetUsersPassword": "No puedes establecer contraseñas de usuarios."
}
}

View File

@@ -0,0 +1,209 @@
{
"chat": {
"title": "Chat",
"description": "Ask questions and get responses from popular AI models with web search and deep thinking.",
"headline": "How can I help you today?",
"composer": {
"placeholder": "What do you want to know?",
"files": {
"add": "Add files",
"remove": "Remove",
"preview": "Preview",
"dropzone": {
"title": "Drop your files here",
"description": "Drop your files here to add them to the conversation"
}
}
},
"new": "Create new chat",
"command": {
"search": "Type a command or search...",
"empty": "No results found."
},
"example": {
"summarize": {
"label": "Summarize",
"prompt": "Please provide a concise summary of the following text, highlighting the key points and main ideas while maintaining the core message. Please ensure the summary is clear and well-structured: The Industrial Revolution was a period of major industrialization and innovation during the late 18th and early 19th centuries. The Industrial Revolution began in Great Britain and quickly spread throughout Western Europe and North America. This period saw the mechanization of manufacturing, development of factory systems, and significant technological innovations in areas like textile production, steam power, and transportation. It led to major social changes including urbanization, the rise of a new working class, and eventual improvements in living standards. However, it also brought challenges like poor working conditions, child labor, and environmental pollution."
},
"analyze": {
"label": "Analyze",
"prompt": "Please analyze this dataset and provide key statistical insights, trends, and patterns. Include relevant metrics, correlations, and any notable outliers: Year,GDP,Population,Life_Expectancy\n1960,543.3,179.3,69.7\n1970,1073.3,203.3,70.8\n1980,2862.5,226.5,73.7\n1990,5980.3,248.7,75.4\n2000,10284.8,281.4,76.8\n2010,14964.4,308.7,78.7\n2020,20893.7,329.5,77.3"
},
"code": {
"label": "Code",
"prompt": "Write a function that takes a list of numbers and returns the sum of all the numbers in the list. The function should be written in Python."
},
"brainstorm": {
"label": "Brainstorm",
"prompt": "Generate a list of 10 creative ideas for a new product or service. Each idea should be unique, innovative, and have a clear market opportunity. Please provide a brief explanation for each idea."
},
"surprise": {
"label": "Surprise me",
"prompt": "Please surprise me with a random fact or interesting tidbit. I'm curious about anything and everything!"
}
}
},
"agent": {
"title": "Agents",
"description": "Personalizable AI agents that operates on your knowledge base and helps with complex tasks.",
"headline": {
"title": "Agents are coming soon!",
"subtitle": "We're working hard to bring you the best AI agents experience. Stay tuned for updates!"
}
},
"image": {
"title": "Image generation",
"description": "Create visuals from text descriptions using different models like SDXL or Midjourney.",
"headline": {
"title": "Describe what you imagine 🖼️",
"subtitle": "And let's make it live."
},
"composer": {
"placeholder": "A cat shipping its startup in minutes, not hours..."
},
"history": {
"title": "History",
"description": "View your image generation history, and see how your images have evolved over time."
},
"generation": {
"new": "Create new",
"goTo": "Go to generation",
"status": {
"idle": "Idle",
"created": "Created",
"loading": "Loading...",
"success": "Completed",
"error": "Failed"
},
"empty": {
"title": "No images yet",
"description": "There are no images generated, start by describing what you want to see."
}
},
"example": {
"fox": {
"label": "Mystic Fox",
"prompt": "A silver fox with glowing eyes emerging from a misty enchanted forest at twilight, surrounded by floating lanterns"
},
"penguin": {
"label": "Dapper Penguin",
"prompt": "A penguin in a tuxedo and monocle sipping tea at an ice bar, with northern lights dancing in the background"
},
"raccoon": {
"label": "Tech Raccoon",
"prompt": "A raccoon wearing glasses and a hoodie, coding on multiple screens in a cozy treehouse office filled with snacks"
},
"elephant": {
"label": "Zen Elephant",
"prompt": "A baby elephant meditating under a giant baobab tree at sunrise, with butterflies landing on its trunk and ears"
},
"dolphin": {
"label": "Cosmic Dolphin",
"prompt": "A dolphin leaping through space, leaving a trail of stardust and nebula colors, with distant planets visible in the background"
}
}
},
"pdf": {
"title": "Chat with PDF",
"description": "Extract insights and answers by chatting directly with your PDF documents.",
"new": "Upload new PDF document",
"recent": "Documentos recientes",
"command": {
"search": "Type a command or search for a document...",
"empty": "No results found."
},
"upload": {
"description": "Upload a PDF file by dragging and dropping it here, from url or by selecting a file. Let's chat!",
"fromDevice": "Select file from device",
"fromUrl": {
"placeholder": "Enter PDF url...",
"cta": "Chat!"
},
"example": {
"cta": "Try with example PDF"
},
"error": {
"api": "Failed to upload the PDF file. Please try again.",
"notFound": "Could not access the PDF file. Please check the URL and try again.",
"unauthorized": "Por favor inicia sesión para subir archivos PDF."
},
"signIn": {
"title": "Inicio de sesión requerido",
"description": "Crea una cuenta gratuita o inicia sesión para subir y chatear con tus documentos PDF.",
"cta": "Iniciar sesión"
},
"success": "The PDF file has been uploaded successfully!",
"confirm": {
"title": "Confirm document details",
"description": "Please confirm the details of the document you are uploading."
},
"loading": {
"title": "Bear with us...",
"description": "We're processing your document, this may take a few seconds."
}
},
"preview": {
"zoom": {
"options": "Zoom options",
"fit": "Page fit",
"in": "Zoom in",
"out": "Zoom out"
},
"navigation": {
"previous": "Previous page",
"next": "Next page"
},
"toggle": "Toggle preview",
"noDocuments": "No documents found to preview, please upload a document or try again later."
},
"composer": {
"placeholder": "Ask me anything about this document...",
"empty": "Start by asking a question. You can also preview the document by clicking on the preview button."
},
"download": {
"success": "The PDF file has been downloaded!"
},
"processing": {
"indexing": "Indexando documento...",
"indexingDescription": "Estamos analizando tu documento para habilitar búsqueda inteligente y Q&A. Esto generalmente toma unos segundos."
},
"suggestions": {
"title": "Prueba preguntar...",
"summarize": "Resume este documento",
"keyPoints": "¿Cuáles son los puntos clave?",
"explain": "Explica los conceptos principales",
"find": "Buscar información específica"
},
"followUp": {
"moreDetail": "Cuéntame más sobre esto",
"clarify": "¿Puedes aclarar eso?",
"continue": "¿Qué más debería saber?"
},
"selection": {
"askAbout": "Preguntar sobre esto"
}
},
"tts": {
"title": "Text to speech",
"description": "Convert text to natural-sounding speech with customizable AI voices.",
"composer": {
"placeholder": "Start typing here or paste any text you want to turn into lifelike speech...",
"settings": {
"voice": {
"speed": {
"description": "Controls the speed of the generated speech. Values below 1.0 will slow down the speech, while values above 1.0 will speed it up. Extreme values may affect the quality of the generated speech."
},
"stability": {
"description": "Increasing stability will make the voice more consistent between re-generations, but it can also make it sounds a bit monotone. On longer text fragments we recommend lowering this value."
},
"similarity": {
"description": "High enhancement boosts overall voice clarity and target speaker similarity. Very high values can cause artifacts, so adjusting this setting to find the optimal value is encouraged."
},
"speakerBoost": {
"description": "Boost the similarity of the synthesized speech and the voice at the cost of some generation speed."
}
}
}
}
}
}

View File

@@ -0,0 +1,351 @@
{
"account": {
"personal": "Cuenta personal",
"settings": {
"title": "Ajustes",
"header": {
"title": "Ajustes de la cuenta",
"description": "Administra tus ajustes y preferencias de cuenta."
},
"security": {
"title": "Seguridad",
"description": "Administra los ajustes de seguridad de tu cuenta."
}
},
"name": {
"edit": {
"description": "Por favor ingresa tu nombre completo o un nombre de usuario con el que te sientas cómodo.",
"success": "¡Nombre actualizado exitosamente!",
"info": "Por favor usa máximo 32 caracteres."
}
},
"avatar": {
"description": "Este es tu avatar.\nHaz clic en el avatar para subir uno personalizado desde tus archivos.",
"info": "Un avatar es opcional pero muy recomendado.",
"update": { "success": "¡Avatar actualizado exitosamente!" },
"remove": { "success": "¡Avatar eliminado exitosamente!" },
"validation": {
"file": "Por favor selecciona un archivo de imagen.",
"size": "El tamaño del archivo debe ser menor a {{size}}.",
"type": "Solo se aceptan archivos {{types}}."
}
},
"accounts": {
"title": "Conexiones",
"description": "Conecta tu cuenta de TurboStarter con un servicio de terceros para usarlo en el inicio de sesión.",
"connect": { "success": "¡Cuenta de {{provider}} conectada!" },
"disconnect": {
"cta": "Desconectar cuenta de {{provider}}",
"success": "¡Cuenta de {{provider}} desconectada!",
"disclaimer": "Estás a punto de eliminar la conexión de inicio de sesión para {{provider}}.\n\nDespués de eliminarla, no podrás usar {{provider}} para iniciar sesión en tu cuenta personal.\n\n¿Deseas continuar?"
},
"info": "Solo accedemos a información básica del perfil para un inicio de sesión seguro.",
"connectedAt": "Conectado el {{date}}"
},
"sessions": {
"title": "Sesiones Activas",
"description": "Administra tus sesiones activas y dispositivos actualmente conectados.",
"info": "Puedes revocar cualquier sesión haciendo clic en el ícono de papelera.",
"revoke": {
"cta": "Revocar sesión",
"success": "¡Sesión revocada exitosamente!"
},
"noSessions": "No se encontraron sesiones activas."
},
"delete": {
"title": "Eliminar cuenta",
"description": "Elimina permanentemente tu cuenta personal y todo su contenido de la plataforma TurboStarter. Esta acción no es reversible, así que por favor continúa con precaución.",
"cta": "Eliminar cuenta",
"disclaimer": "Estás a punto de eliminar tu cuenta personal.\n\nDespués de eliminar tu cuenta, todos tus datos serán eliminados permanentemente y no podrán ser recuperados. Te enviaremos un enlace de confirmación para verificar esta acción.\n\n¿Deseas continuar?",
"email": {
"subject": "Lamentamos verte partir",
"preview": "Elimina tu cuenta de TurboStarter",
"body": "Haz clic en el botón de abajo para eliminar permanentemente tu cuenta.",
"or": "O copia y pega este enlace en tu navegador:",
"cta": "Eliminar cuenta",
"disclaimer": "Si no solicitaste la eliminación de la cuenta, puedes ignorar este correo."
},
"confirmation": {
"cta": "Enviar enlace de confirmación",
"success": "¡Enlace de confirmación enviado!"
}
},
"email": {
"change": {
"description": "Ingresa el correo electrónico que deseas usar para iniciar sesión. Se usará para notificaciones relacionadas con la cuenta.",
"success": "¡Revisa tu bandeja de entrada para confirmar tu nuevo correo!",
"info": "El correo debe verificarse antes de poder usarlo.",
"email": {
"subject": "Confirma el cambio de correo electrónico",
"preview": "Confirma el cambio de correo electrónico para TurboStarter",
"body": "Estás solicitando cambiar tu correo electrónico a <bold>{{newEmail}}</bold>. Haz clic en el botón de abajo para confirmar el cambio.",
"or": "O copia y pega este enlace en tu navegador:",
"cta": "Confirmar cambio de correo",
"disclaimer": "Si no solicitaste el cambio de correo, puedes ignorar este correo."
}
},
"confirm": {
"loading": "Enviando correo...",
"cta": "Verificar correo",
"email": {
"subject": "Confirma tu dirección de correo electrónico",
"preview": "Confirma tu dirección de correo electrónico para TurboStarter",
"body": "¡Un paso más! Haz clic en el botón de abajo para verificar tu dirección de correo electrónico.",
"or": "O copia y pega este enlace en tu navegador:",
"cta": "Confirmar correo electrónico",
"disclaimer": "Si no te registraste para una cuenta, puedes ignorar este correo.",
"sent": "¡Correo de verificación enviado!"
}
}
},
"password": {
"update": {
"title": "Actualizar contraseña",
"description": "Cambia tu contraseña aquí. Debe contener una letra mayúscula, un caracter especial, un número y al menos 8 caracteres.",
"info": "Intenta usar una frase memorable con caracteres mixtos.",
"header": {
"title": "Actualizar contraseña",
"description": "Ingresa tu nueva contraseña"
},
"email": {
"subject": "Restablece tu contraseña",
"preview": "Restablece tu contraseña para TurboStarter",
"body": "¿Olvidaste tu contraseña? Haz clic abajo para restablecerla.",
"or": "O copia y pega este enlace en tu navegador:",
"cta": "Restablecer contraseña",
"disclaimer": "Si no solicitaste restablecer la contraseña, puedes ignorar este correo."
},
"cta": "Actualizar contraseña",
"noPassword": "No tienes contraseña. Usa el flujo de <bold>contraseña olvidada</bold> para crear una.",
"success": "¡Contraseña actualizada exitosamente!"
},
"forgot": {
"title": "Contraseña olvidada",
"label": "¿Olvidaste tu contraseña?",
"header": {
"title": "¿Olvidaste tu contraseña?",
"description": "Ingresa tu correo y te enviaremos un enlace"
},
"success": {
"title": "¡Éxito!",
"description": "Te enviamos un enlace para restablecerla."
},
"cta": "Enviar enlace de restablecimiento",
"back": "Volver a iniciar sesión"
},
"require": {
"title": "Reautenticación",
"description": "Esta operación requiere tu contraseña."
}
},
"passkeys": {
"title": "Llaves de acceso",
"description": "Usa llaves de acceso como alternativa segura a las contraseñas.",
"info": "Usan biometría o PIN del dispositivo para mayor seguridad.",
"addedAt": "Añadida el {{date}}",
"type": {
"singleDevice": "Dispositivo único",
"multiDevice": "Multidispositivo"
},
"add": {
"cta": "Añadir llave de acceso",
"success": "¡Añadida exitosamente!"
},
"remove": {
"cta": "Eliminar llave de acceso",
"success": "¡Eliminada exitosamente!",
"disclaimer": "Estás a punto de eliminar una llave de acceso.\n\nTras eliminarla, no podrás usarla para iniciar sesión.\n\n¿Deseas continuar?"
}
},
"twoFactor": {
"title": "Autenticación de dos factores",
"description": "Agrega una capa adicional de seguridad requiriendo un código enviado a tu correo o teléfono.",
"info": "Es opcional (pero recomendado) y puede habilitarse o deshabilitarse en cualquier momento.",
"enable": {
"title": "Habilitar 2FA",
"description": "Una vez habilitado, deberás proporcionar dos métodos de autenticación. Proporciona tu contraseña actual abajo.",
"success": "¡2FA habilitado exitosamente!"
},
"disable": {
"title": "Deshabilitar 2FA",
"description": "¿Seguro que quieres deshabilitarla? Tu TOTP dejará de funcionar y tus códigos de recuperación serán invalidados.",
"success": "¡2FA deshabilitado exitosamente!"
},
"totp": {
"title": "App autenticadora (TOTP)",
"description": "Genera códigos usando apps como Google Authenticator u Okta Verify.",
"enable": {
"title": "Habilitar app autenticadora",
"description": "Escanea el QR y luego ingresa el código de 6 dígitos."
},
"success": "¡Código verificado!"
},
"backupCodes": {
"title": "Códigos de recuperación",
"description": "Códigos de seguridad cuando no puedes acceder a otros métodos.",
"save": {
"title": "Códigos de recuperación",
"description": "Copia y guarda estos códigos."
}
}
}
},
"password": "Contraseña",
"currentPassword": "Contraseña actual",
"hidePassword": "Ocultar contraseña",
"showPassword": "Mostrar contraseña",
"newPassword": "Nueva contraseña",
"divider": "o continuar con",
"rememberMe": "Recordarme",
"logout": {
"cta": "Cerrar sesión",
"confirm": "¿Seguro que quieres cerrar sesión?"
},
"register": {
"title": "Registro",
"header": {
"title": "Crear una cuenta",
"description": "Completa el formulario para crear tu cuenta"
},
"cta": "Registrarse",
"alreadyHaveAccount": "¿Ya tienes una cuenta?",
"success": {
"title": "¡Éxito!",
"description": "¡Registro exitoso! Verifica tu correo.",
"notification": "¡Éxito! ¡Verifica tu correo!"
}
},
"login": {
"title": "Iniciar sesión",
"header": {
"title": "¡Bienvenido de nuevo! 👋",
"description": "Ingresa tus datos abajo"
},
"magicLink": {
"label": "Enlace mágico",
"cta": "Enviarme un enlace mágico",
"success": {
"title": "¡Éxito!",
"description": "Te enviamos un enlace mágico por correo."
},
"email": {
"preview": "Inicia sesión con enlace mágico",
"subject": "Tu enlace mágico 🪄",
"body": "¡Casi ahí! Haz clic abajo para iniciar sesión.",
"or": "O copia y pega este enlace en tu navegador:",
"cta": "Iniciar sesión con enlace mágico",
"disclaimer": "Si no intentaste iniciar sesión, ignora este correo."
}
},
"twoFactor": {
"totp": {
"header": {
"title": "Autenticación de dos factores",
"description": "Abre tu app autenticadora y escribe el código."
},
"cta": "Usar una app autenticadora"
},
"trustDevice": "Confiar en este dispositivo",
"backupCode": {
"header": {
"title": "Autenticación de dos factores",
"description": "Ingresa un código de recuperación."
},
"placeholder": "Código de recuperación",
"cta": "Usar un código de recuperación"
}
},
"password": {
"loading": "Iniciando sesión...",
"success": "¡Sesión iniciada!"
},
"phone": { "label": "Teléfono" },
"anonymous": { "cta": "Continuar como invitado" },
"passkey": { "cta": "Iniciar sesión con llave de acceso" },
"social": "Iniciar sesión con <capitalize>{{provider}}</capitalize>",
"cta": "Iniciar sesión",
"noAccount": "¿No tienes una cuenta?"
},
"goToLogin": "Ir a inicio de sesión",
"banReason": "Motivo de ban",
"banExpiresIn": "Expira en",
"updateRole": "Actualizar rol",
"error": {
"cannotSetNonExistentValue": "No puedes establecer un valor que no existe.",
"authenticationFailed": "No pudimos autenticarte. Intenta de nuevo.",
"noDataToUpdate": "No hay datos para actualizar.",
"user": {
"cannotImpersonateAdmins": "No puedes impersonar a admins.",
"notFound": "No pudimos encontrar tu cuenta. Verifica tus credenciales.",
"alreadyExists": "Ya existe una cuenta con este correo.",
"infoNotFound": "No pudimos recuperar la información de tu cuenta.",
"emailNotFound": "No se encontró una cuenta con este correo.",
"accountNotFound": "No pudimos encontrar tu cuenta.",
"alreadyHasPassword": "Ya tienes una contraseña. Usa <bold>contraseña olvidada</bold> para restablecerla.",
"cannotBanYourself": "No puedes banearte a ti mismo.",
"cannotChangeRole": "No puedes cambiar tu rol.",
"banned": "Estás baneado.",
"cannotGetUser": "No puedes obtener el usuario.",
"cannotRemoveYourself": "No puedes eliminarte a ti mismo.",
"alreadyExistsUseAnotherEmail": "Ya existe una cuenta con este correo. Usa otro."
},
"session": {
"creation": "No se pudo iniciar tu sesión. Intenta de nuevo.",
"retrieval": "No se pudo recuperar tu sesión. Inicia sesión nuevamente.",
"expired": "Tu sesión ha expirado. Inicia sesión para continuar."
},
"account": {
"creation": "Error al crear tu cuenta.",
"update": "No pudimos actualizar tu cuenta."
},
"credentials": {
"notFound": "No pudimos encontrar tus credenciales.",
"password": {
"invalid": "Contraseña incorrecta.",
"tooShort": "Mínimo 8 caracteres.",
"tooLong": "Máximo 100 caracteres.",
"compromised": "Contraseña comprometida. Usa otra."
},
"email": {
"invalid": "Correo inválido.",
"invalidFormat": "Correo inválido.",
"notVerified": "Verifica tu correo antes de continuar.",
"cannotUpdate": "Este correo no puede actualizarse."
},
"invalidEmailOrPassword": "Correo o contraseña incorrectos."
},
"social": {
"alreadyLinked": "Cuenta social ya conectada a otro usuario.",
"providerNotFound": "Método no disponible.",
"unlinkLastAccount": "Debes mantener al menos un método conectado."
},
"token": {
"invalid": "Token inválido.",
"idNotSupported": "Método no soportado."
},
"passkey": {
"challengeNotFound": "Desafío no encontrado.",
"notAllowed": "No permitido registrar esta llave.",
"verificationFailed": "No se pudo verificar.",
"notFound": "Llave no encontrada.",
"updateFailed": "No se pudo actualizar."
},
"anonymous": {
"cannotSignInAgain": "Usuarios anónimos no pueden iniciar sesión de nuevo."
},
"otp": {
"notEnabled": "Debes habilitar OTP.",
"expired": "OTP expirado. Solicita uno nuevo."
},
"totp": { "notEnabled": "Debes habilitar TOTP." },
"twoFactor": {
"notEnabled": "Debes habilitar 2FA.",
"invalidCookie": "Cookie de 2FA inválida."
},
"backupCodes": { "notEnabled": "Debes habilitar códigos de respaldo." },
"code": {
"invalid": "Código inválido.",
"tooManyAttempts": "Demasiados intentos. Intenta más tarde."
}
}
}

View File

@@ -0,0 +1,108 @@
{
"currency": "eur",
"pricing": {
"label": "Precios",
"title": "Planes simples para todos",
"description": "Elige el plan que se adapte a tus necesidades y comienza tu camino hacia el éxito."
},
"manage": {
"plan": {
"title": "Gestionar plan",
"description": "Revisa los detalles de tu plan actual. Puedes cambiar tu plan o cancelar tu suscripción en cualquier momento."
},
"billing": {
"title": "Gestionar facturación",
"description": "Visita el Portal de Facturación para gestionar tu suscripción y facturación. Puedes actualizar tu método de pago, cancelar tu suscripción, descargar facturas y más.",
"visitPortal": "Visitar Portal de Facturación"
},
"description": "Gestiona tu facturación y suscripción."
},
"subscribe": "Suscribirse ahora",
"updatePlan": "Actualizar plan",
"getLifetimeAccess": "Obtener acceso de por vida",
"interval": {
"day": "día",
"week": "semana",
"month": "mes",
"year": "año",
"lifetime": "de por vida"
},
"trial": {
"cta": "Comenzar gratis",
"period": "Prueba con {{period}} días gratis"
},
"discount": {
"specialOffer": "OFERTA ESPECIAL: <bold>-{{discount}} de descuento</bold>"
},
"plan": {
"starter": {
"name": "Inicial",
"description": "Para pequeños proyectos que están comenzando",
"features": {
"sync": "Sincronización perfecta",
"basicSupport": "Soporte básico",
"limitedStorage": "Almacenamiento limitado",
"emailNotifications": "Notificaciones por correo",
"basicReports": "Informes básicos"
}
},
"premium": {
"name": "Premium",
"description": "Perfecto para equipos en crecimiento con funciones avanzadas",
"badge": "Más vendido",
"features": {
"advancedSync": "Sincronización avanzada",
"prioritySupport": "Soporte prioritario",
"moreStorage": "Más almacenamiento",
"teamCollaboration": "Colaboración en equipo",
"smsNotifications": "Notificaciones SMS",
"advancedReports": "Informes avanzados"
}
},
"enterprise": {
"name": "Empresarial",
"description": "Diseñado para organizaciones y grandes equipos",
"features": {
"unlimitedStorage": "Almacenamiento ilimitado",
"customBranding": "Marca personalizada",
"dedicatedSupport": "Soporte dedicado",
"apiAccess": "Acceso a API",
"userRoles": "Gestión de roles de usuario",
"auditLogs": "Registros de auditoría",
"singleSignOn": "Inicio de sesión único",
"advancedAnalytics": "Análisis avanzado"
}
}
},
"error": {
"checkout": "Ocurrió un error durante el proceso de pago.",
"checkoutRetrieve": "Ocurrió un error al recuperar la sesión de pago.",
"portal": "Ocurrió un error al obtener la URL del portal.",
"invalidProvider": "¡Proveedor de facturación inválido!",
"lemonSqueezy": "¡Error interno de la API de Lemon Squeezy!",
"priceNotFound": "¡Precio no encontrado!",
"customerNotFound": "¡Cliente no encontrado!",
"orderNotFound": "¡Pedido no encontrado!",
"subscriptionNotFound": "¡Suscripción no encontrada!",
"webhook": {
"signatureNotFound": "¡Firma del webhook no encontrada!",
"signatureInvalid": "Firma del webhook inválida.",
"metaInvalid": "Meta del webhook inválido.",
"dataInvalid": "Datos del webhook inválidos.",
"unsupportedEvent": "Evento de webhook no soportado.",
"unhandledEvent": "Evento de webhook no manejado."
},
"customerCreation": "Error al crear el cliente.",
"promotionCodeRetrieve": "No se pudo recuperar el código de promoción."
},
"status": {
"active": "Activo",
"canceled": "Cancelado",
"incomplete": "Incompleto",
"incompleteExpired": "Incompleto expirado",
"pastDue": "Vencido",
"paused": "Pausado",
"trialing": "En prueba",
"unpaid": "Sin pagar"
}
}

View File

@@ -0,0 +1,306 @@
{
"apps": "Aplicaciones",
"freeTools": "Herramientas gratuitas",
"legal": {
"label": "Legal",
"privacy": "Política de privacidad",
"terms": "Términos y condiciones",
"copyright": "Todos los derechos reservados"
},
"product": "Producto",
"success": "¡Éxito!",
"auth": "Autenticación",
"menu": "Menú",
"open": "Abrir",
"lastUsed": "Último uso",
"code": "Código",
"security": "Seguridad",
"builtWith": "Construido con",
"about": "Acerca de",
"remove": "Eliminar",
"update": "Actualizar",
"dismiss": "Descartar",
"install": "Instalar",
"contactUs": "Contáctanos",
"checkForUpdates": "Verificar actualizaciones",
"tryAgain": "Intentar de nuevo",
"reset": "Restablecer",
"resources": "Recursos",
"or": "O",
"more": "Más",
"close": "Cerrar",
"toggle": "Alternar",
"enable": "Habilitar",
"disable": "Deshabilitar",
"download": "Descargar",
"edit": "Editar",
"add": "Añadir",
"regenerate": "Regenerar",
"documentation": "Documentación",
"done": "Listo",
"chat": "Chat",
"copy": "Copiar",
"getStarted": "Comenzar",
"goToDashboard": "Ir al panel",
"home": "Inicio",
"ai": "IA",
"aiTools": "Herramientas IA",
"settings": "Ajustes",
"billing": "Facturación",
"blog": "Blog",
"support": "Soporte",
"feedback": "Comentarios",
"dashboard": "Panel",
"platform": "Plataforma",
"account": "Cuenta",
"continue": "Continuar",
"cancel": "Cancelar",
"save": "Guardar",
"verify": "Verificar",
"slug": "Slug",
"verified": "Verificado",
"unverified": "No verificado",
"addNew": "Añadir nuevo",
"addMore": "Añadir más",
"connect": "Conectar",
"emojai": "Emojai",
"enabled": "Habilitado",
"envin": "Envin",
"extro": "Extro",
"notEnabled": "No habilitado",
"disabled": "Deshabilitado",
"date": "Fecha",
"role": "Rol",
"joinedAt": "Unido el",
"createdAt": "Creado el",
"expiresAt": "Expira el",
"expires": "Expira",
"expired": "Expirado",
"review": "Revisar",
"resend": "Reenviar",
"status": "Estado",
"pending": "Pendiente",
"accepted": "Aceptado",
"canceled": "Cancelado",
"rejected": "Rechazado",
"accept": "Aceptar",
"reject": "Rechazar",
"first": "Primero",
"last": "Último",
"previous": "Anterior",
"next": "Siguiente",
"noResults": "No hay resultados",
"rowsPerPage": "Filas por página",
"rowsSelected": "{{selected}} de {{total}} fila(s) seleccionada(s)",
"selectDate": "Seleccionar fecha",
"selectDateRange": "Seleccionar rango de fechas",
"pageOf": "Página {{page}} de {{total}}",
"selected": "seleccionada",
"clear": "Limpiar",
"slider": "Slider",
"from": "Desde",
"to": "Hasta",
"sessions": "Sesiones",
"revokeAll": "Revocar todas",
"leave": "Leave",
"view": "Ver",
"searchColumns": "Buscar columnas",
"asc": "Asc",
"desc": "Desc",
"hide": "Ocultar",
"logo": "Logo",
"disconnect": "Desconectar",
"accounts": "Cuentas",
"provider": "Proveedor",
"credential": "Credencial",
"banned": "Baneado",
"notBanned": "No baneado",
"ban": "Banear",
"unban": "Desbanear",
"impersonate": "Impersonar",
"impersonating": "Impersonando",
"anonymous": "Anónimo",
"member": "Miembro",
"user": "Usuario",
"admin": "Administrador",
"owner": "Propietario",
"filter": "Filtrar",
"you": "Tú",
"allRoles": "Todos los roles",
"delete": "Eliminar",
"invite": "Invitar",
"avatar": "Avatar",
"learnMore": "Más información",
"upgrade": "Mejorar",
"updatedAt": "Actualizado el",
"visitors": "Visitantes",
"desktop": "Escritorio",
"mobile": "Móvil",
"other": "Otro",
"pdf": "PDF",
"views": "Vistas",
"users": "Usuarios",
"customers": "Clientes",
"plan": "Plan",
"plans": "Planes",
"id": "ID",
"ideasGenerator": "Generador de Ideas",
"image": "Imagen",
"searchPlaceholder": "Buscar...",
"organization": "Organización",
"organizations": "Organizaciones",
"members": "Miembros",
"memberships": "Membresías",
"invitations": "Invitaciones",
"actions": "Acciones",
"affiliates": "Afiliados",
"agent": "Agente",
"lastDays": "Últimos {{count}} días",
"lastMonths_one": "Últimos mes",
"lastMonths_other": "Últimos {{count}} meses",
"selectMonth": "Seleccionar mes",
"seeRoadmap": "Ver roadmap",
"general": "General",
"notifications": "Notificaciones",
"rate": "Calificar",
"share": "Compartir",
"syncreads": "SyncReads",
"privacy": "Privacidad",
"reason": "Motivo",
"expiresIn": "Expira en",
"never": "Nunca",
"language": {
"label": "Idioma",
"description": "Elige un idioma para tu aplicación.",
"info": "Esto cambiará el idioma de la aplicación. No cambiará el idioma del navegador.",
"change": "Cambiar idioma"
},
"theme": {
"title": "Tema",
"customization": {
"title": "Personalizar",
"label": "Personalizar tema",
"description": "Elige un estilo y color para tu aplicación."
},
"color": {
"label": "Color",
"orange": "Naranja",
"rose": "Rosa",
"red": "Rojo",
"yellow": "Amarillo",
"gray": "Gris",
"stone": "Piedra",
"green": "Verde",
"blue": "Azul",
"violet": "Violeta"
},
"mode": {
"label": "Modo",
"system": "Sistema",
"light": "Claro",
"dark": "Oscuro"
}
},
"name": "Nombre",
"message": "Mensaje",
"email": "Correo electrónico",
"skip": "Omitir",
"finish": "Finalizar",
"two-factor": "Autenticación de dos factores",
"error": {
"title": "¡Ups!",
"general": "Algo salió mal, por favor intenta de nuevo.",
"apologies": "Lo sentimos, ocurrió un error al procesar tu solicitud. Por favor, contáctanos si el problema persiste.",
"invalid": "¡Datos inválidos!",
"notFound": "¡No encontrado!",
"resourceDoesNotExist": "El recurso que estás buscando no existe.",
"unauthorized": "¡Necesitas iniciar sesión para acceder a esta función!",
"forbidden": "¡No tienes permiso para acceder a esta función!",
"insufficientCredits": "Créditos insuficientes",
"rateLimit": "Límite de solicitudes excedido"
},
"search": {
"label": "Buscar",
"inProgress": "Buscando...",
"completed": "Búsqueda completada"
},
"greeting": {
"morning": "Buenos días",
"afternoon": "Buenas tardes",
"evening": "Buenas tardes",
"night": "Buenas noches"
},
"like": "Me gusta",
"dislike": "No me gusta",
"reload": "Recargar",
"creditsLeft": "Créditos restantes",
"credits": {
"description": "Tienes {{count}} créditos restantes",
"description2": "Los créditos se usan para operaciones de IA",
"description3": "Mejora tu plan para más créditos",
"cta": "Obtener más créditos"
},
"rateLimit": {
"description": "Has alcanzado tu límite de solicitudes",
"description2": "Por favor espera antes de hacer más solicitudes",
"description3": "Mejora tu plan para límites más altos",
"cta": "Mejorar plan"
},
"goBackHome": "Volver al inicio",
"history": "Historial",
"current": "Actual",
"stop": "Detener",
"send": "Enviar",
"record": "Grabar",
"recording": "Grabando...",
"processing": "Procesando",
"transcribing": "Transcribiendo...",
"microphoneDenied": "Acceso al micrófono denegado",
"transcriptionFailed": "La transcripción falló. Por favor, inténtalo de nuevo.",
"pressEscapeToCancel": "Presiona Escape para cancelar",
"newTab": "Abrir en nueva pestaña",
"results": "Resultados",
"analyzingImage": "Analizando imagen...",
"reasoning": {
"inProgress": "Pensando...",
"completed": "Razonamiento completado"
},
"theme.toggle": "Alternar tema",
"tts": "Texto a Voz",
"speed": "Velocidad",
"slower": "Más lento",
"faster": "Más rápido",
"stability": "Estabilidad",
"moreVariable": "Más variable",
"moreStable": "Más estable",
"similarity": "Similitud",
"low": "Bajo",
"high": "Alto",
"speakerBoost": "Mejora de voz",
"pause": "Pausar",
"play": "Reproducir",
"second_one": "{{count}} segundo",
"second_other": "{{count}} segundos",
"square": "Cuadrado",
"standard": "Estándar",
"landscape": "Paisaje",
"portrait": "Retrato",
"model": "Modelo",
"aspectRatio": "Proporción",
"count": "Cantidad",
"completedAt": "Completado el",
"details": "Detalles",
"file": "Archivo",
"size": "Tamaño",
"type": "Tipo",
"confirm": "Confirmar",
"downloading": "Descargando...",
"canvases": "Lienzos",
"threads": "Hilos",
"briefing": "Resumen",
"connections": "Conexiones",
"vocabulary": "Vocabulario",
"dataHealth": "Salud de Datos",
"demos": "Demos",
"dev": "Desarrollo"
}

View File

@@ -0,0 +1,26 @@
{
"user": {
"home": {
"title": "Panel",
"description": "Una vista general de la actividad de tu cuenta personal en todos tus proyectos."
}
},
"organization": {
"home": {
"title": "Panel de la organización",
"description": "Una vista general de la actividad y rendimiento de tu organización en todos los proyectos."
}
},
"chart": {
"showing": "Mostrando visitantes totales del último período",
"trending": "Tendencia de hasta 5.2% este mes",
"period": "Enero - Junio 2024",
"bar": "Gráfico de barras",
"pie": "Gráfico circular",
"area": "Gráfico de área",
"line": "Gráfico de líneas",
"shape": "Gráfico de formas",
"radial": "Gráfico radial",
"radar": "Gráfico de radar"
}
}

View File

@@ -0,0 +1,14 @@
export const es = {
common: async () => {
await import("dayjs/locale/es");
return import("./common.json");
},
auth: () => import("./auth.json"),
billing: () => import("./billing.json"),
marketing: () => import("./marketing.json"),
organization: () => import("./organization.json"),
admin: () => import("./admin.json"),
dashboard: () => import("./dashboard.json"),
validation: () => import("./validation.json"),
ai: () => import("./ai.json"),
} as const;

View File

@@ -0,0 +1,251 @@
{
"product": {
"title": "Esta podría ser tu propia startup de 1 millón de dólares",
"description": "Una aplicación TurboStarter totalmente personalizable. Sustituye tu contenido y lanza tu producto más rápido que nunca.",
"mobile": {
"ios": {
"title": "App para iOS",
"description": "Experiencia nativa en iPhone y iPad"
},
"android": {
"title": "App para Android",
"description": "Optimizada para teléfonos Android"
}
},
"extension": {
"chrome": {
"title": "Extensión de Chrome",
"description": "Integración con el navegador Chrome"
},
"firefox": {
"title": "Complemento de Firefox",
"description": "Navegación mejorada para usuarios de Firefox"
},
"edge": {
"title": "Complemento de Edge",
"description": "Herramientas potentes para Microsoft Edge"
}
}
},
"announcement": "Aquí va el anuncio de tu próxima función",
"shippedWith": "Incluye herramientas listas para producción",
"cta": {
"question": "¿Listo para empezar?",
"button": "Empieza gratis",
"buy": {
"question": "¿Listo para empezar?",
"description": "Compra para desbloquear el acceso completo a la base de código y empieza a lanzar tu producto hoy mismo. En todas las plataformas.",
"button": "Obtén acceso",
"join": {
"description": "Únete a otros fundadores que personalizan TurboStarter, comparte tácticas de lanzamiento y mantente al tanto de las novedades.",
"button": "Únete a la comunidad"
}
}
},
"editToReload": "Edita <code>{{file}}</code> y guarda para recargar.",
"workInProgress": {
"title": "¡Estamos trabajando en ello!",
"description": "Actualmente {{feature}} no está disponible. Estamos trabajando en ello y estará disponible pronto."
},
"features": {
"label": "Características",
"title": "Convierte a los visitantes en clientes",
"description": "Todo lo que necesitas para crear, desplegar y escalar tus aplicaciones con confianza.",
"feature": {
"core": {
"title": "Base fundamental",
"description": "Autenticación, base de datos y API ya conectadas para que puedas enfocarte en aportar valor a tus clientes."
},
"ai": {
"title": "Capacidades impulsadas por IA",
"description": "Funciones inteligentes con recomendaciones y flujos de trabajo automatizados impulsados por IA."
},
"mobile": {
"title": "Experiencia móvil nativa",
"description": "Aplicación móvil completamente equipada con sincronización perfecta en todos tus dispositivos."
},
"extension": {
"title": "Extensión de navegador",
"description": "Mantén a los usuarios comprometidos en su navegador con accesos directos personalizables a tu producto."
}
}
},
"testimonials": {
"label": "Testimonios",
"title": "Los equipos lanzan más rápido con nosotros",
"description": "Conoce a fundadores y desarrolladores que personalizaron el starter para lanzar antes y convertir mejor.",
"rating": "{{rating}} de {{count}}+ reseñas",
"reviews": {
"jack": {
"name": "Jack",
"position": "Desarrollador Full Stack",
"body": "El starter liberó a nuestro equipo del trabajo repetitivo. Sustituimos el copy en un fin de semana y nos concentramos en las funciones que los clientes realmente querían."
},
"jill": {
"name": "Jill",
"position": "Fundadora de startup",
"body": "Lanzamos nuestro MVP en dos semanas. Todo, desde las páginas de marketing hasta la autenticación, estaba listo; solo reescribimos el copy para nuestra audiencia."
},
"john": {
"name": "John",
"position": "Desarrollador senior",
"body": "La experiencia de desarrollador es incomparable. El stack funciona desde el primer momento y se mantiene lo suficientemente flexible como para adaptarse a la narrativa de nuestro producto."
},
"sarah": {
"name": "Sarah",
"position": "Product Manager",
"body": "Escalamos del prototipo a miles de usuarios sin complicaciones. La analítica, la facturación y los flujos de marketing estaban listos para personalizar."
},
"mike": {
"name": "Mike",
"position": "Líder de ingeniería",
"body": "La comunidad es excelente. Respuestas rápidas, feedback para los lanzamientos y mejoras constantes mantienen a nuestro equipo avanzando."
}
}
},
"faq": {
"label": "Preguntas frecuentes",
"title": "¿Tienes una pregunta? Tenemos respuestas",
"description": "Encuentra respuestas a preguntas frecuentes sobre funciones, precios, implementación y cómo empezar con nuestra plataforma.",
"cta": "Contáctanos",
"question": {
"whatDoesOurPlatformDo": {
"question": "¿Qué hace nuestra plataforma?",
"answer": "TurboStarter te ofrece una base SaaS full-stack: sitio de marketing, aplicación web, apps móviles y extensiones, para que puedas enfocarte en la experiencia única de tus clientes."
},
"howWillThisBenefitMyBusiness": {
"question": "¿Cómo beneficiará esto a mi negocio?",
"answer": "Lanzas más rápido, entregas funciones antes y conviertes más leads porque el producto base, la analítica y los flujos de facturación ya están listos."
},
"isMyDataSafe": {
"question": "¿Mis datos están seguros?",
"answer": "Sí. Seguimos prácticas de seguridad de nivel empresarial y ofrecemos salvaguardas para que adaptes las políticas a los requisitos de tu industria."
},
"whatKindOfIntegrationsAreAvailable": {
"question": "¿Qué tipo de integraciones están disponibles?",
"answer": "TurboStarter incluye desde el inicio integraciones SaaS populares, y su arquitectura modular facilita añadir tu propio stack."
},
"howEasyIsItToOnboardMyTeam": {
"question": "¿Qué tan fácil es incorporar a mi equipo?",
"answer": "Tu equipo puede incorporarse rápidamente con documentación clara, patrones TypeScript-first y ejemplos que muestran exactamente dónde personalizar."
},
"whatTypesOfBusinessesCanUseThis": {
"question": "¿Qué tipos de empresas pueden usar esto?",
"answer": "Desde fundadores individuales hasta equipos de innovación empresarial, TurboStarter se adapta a cualquier caso de uso SaaS que necesite una ventaja inicial."
},
"canICustomizeThisToFitMyBusinessNeeds": {
"question": "¿Puedo personalizar esto para adaptarlo a las necesidades de mi negocio?",
"answer": "Por supuesto. Cada módulo está diseñado para personalizarse rápidamente: actualiza el copy, cambia componentes y amplía la lógica sin pelearte con el framework."
}
}
},
"contact": {
"label": "Contacto",
"title": "Ponte en contacto con nosotros hoy para soporte",
"cta": "Hablar con ventas",
"description": "Contáctanos para recibir ayuda o compartir comentarios",
"form": {
"name": {
"placeholder": "Juan Pérez"
},
"email": {
"placeholder": "tu@correo.com"
},
"message": {
"placeholder": "¿Cómo podemos ayudarte?"
},
"submit": "Enviar mensaje",
"success": {
"title": "¡Mensaje enviado!",
"description": "Gracias por tu mensaje. Te responderemos pronto."
}
},
"email": {
"subject": "Has recibido un nuevo envío del formulario de contacto",
"body": "Nuevo envío del formulario de contacto"
}
},
"blog": {
"label": "Blog",
"title": "Noticias y actualizaciones sobre la plataforma",
"description": "Mantente al día con las últimas noticias, ideas y actualizaciones de nuestra plataforma. Descubre nuevas funciones, buenas prácticas y tendencias del sector.",
"timeToRead": "{{time}} min de lectura",
"tag": {
"learning": "aprendizaje",
"skills": "habilidades",
"progress": "progreso",
"product": "producto",
"development": "desarrollo",
"prototype": "prototipo",
"launch": "lanzamiento"
}
},
"api": {
"title": "Referencia de API",
"description": "Crea integraciones con nuestra API"
},
"roadmap": {
"title": "Hoja de ruta",
"description": "Descubre lo que viene después"
},
"docs": {
"title": "Documentación",
"description": "Aprende a usar TurboStarter"
},
"ai": {
"description": "Libera productividad e innovación con el asistente de IA de TurboStarter. Obtén respuestas al instante, automatiza tareas y potencia tu flujo de trabajo con inteligencia artificial avanzada, segura, confiable y diseñada para equipos modernos.",
"prompt": {
"history": "Cuenta la historia de internet",
"capitals": "Hazme un cuestionario sobre las capitales del mundo",
"quantum": "Explica la computación cuántica",
"realWorld": "Describe un caso real de IA"
},
"placeholder": "Haz una pregunta...",
"cta": "Enviar"
},
"setup": {
"steps": {
"step": {
"start": {
"title": "Este es un paso simple",
"description": "Necesitas presionar el botón Continuar para proceder al siguiente paso"
},
"required": {
"title": "Este es un paso requerido",
"description": "Tienes que completar todos los campos requeridos para proceder al siguiente paso",
"fields": {
"data": "Entiendo que mis datos son privados y están encriptados",
"privacy": "He leído y acepto la <a>Política de Privacidad</a>"
}
},
"skip": {
"title": "Este es un paso que puedes omitir",
"description": "Puedes omitir este paso y proceder al siguiente paso"
},
"final": {
"title": "Este es el paso final",
"description": "Has completado todos los pasos, ¡disfruta de tu nueva aplicación!"
}
}
}
},
"update": {
"available": {
"title": "Nueva actualización disponible!",
"description": "Una nueva actualización está lista para ser instalada. Haz clic abajo para instalarla o descarta para más tarde."
},
"installing": {
"title": "Instalando actualización...",
"description": "La actualización está siendo instalada. Por favor, espera un momento."
}
}
}

View File

@@ -0,0 +1,188 @@
{
"create": {
"title": "Crear organización",
"description": "Crea una nueva organización para gestionar tu equipo y proyectos.",
"cta": "Crear organización",
"info": "El nombre de tu organización debe ser único y descriptivo."
},
"join": {
"title": "Unirse a una organización",
"description": "Únete a una organización para colaborar con tu equipo, gestionar proyectos eficientemente y acceder a recursos compartidos."
},
"settings": {
"title": "Configuración",
"header": {
"title": "Configuración de la organización",
"description": "Gestiona la configuración y preferencias de tu organización."
}
},
"invitations": {
"title": "Invitaciones",
"disclaimer": {
"title": "Has sido invitado a unirte a una organización",
"description": "Inicia sesión o crea una cuenta para aceptar la invitación y unirte a la organización."
},
"resend": { "success": "¡Invitación reenviada exitosamente!" },
"cancel": { "success": "¡Invitación cancelada exitosamente!" },
"accept": { "success": "¡Ahora eres miembro de {{organization}}!" },
"reject": { "success": "Has rechazado unirte a {{organization}}." },
"expired": {
"title": "Invitación no encontrada o expirada",
"description": "La invitación que estás buscando está expirada o no existe. Contacta al propietario de la organización para renovar la invitación.",
"cta": "Volver al panel"
},
"emailMismatch": {
"title": "Estás conectado con una cuenta diferente",
"description": "El correo que intentas usar no coincide con el correo de la invitación. Inicia sesión como <bold>{{email}}</bold> o contacta al propietario para actualizar la invitación.",
"cta": "Iniciar sesión como {{email}}",
"skip": "Ir al panel"
},
"invitation": {
"title": "Unirse a {{organizationName}}",
"description": "<bold>{{inviterEmail}}</bold> te ha invitado a unirte a <bold>{{organizationName}}</bold> en TurboStarter.",
"skip": "Ir al panel"
},
"user": {
"banner": {
"title_one": "Tienes una invitación pendiente",
"title_other": "Tienes {{count}} invitaciones pendientes",
"description": "Haz clic para revisar los detalles y aceptar o rechazar la invitación."
},
"list": {
"title": "Invitaciones pendientes",
"description": "Ver y gestionar tus invitaciones pendientes."
}
}
},
"members": {
"title": "Miembros",
"header": {
"title": "Miembros de la organización",
"description": "Gestiona los miembros de tu organización, invitaciones y roles."
},
"invite": {
"title": "Invitar miembro",
"description": "Para agregar un nuevo miembro a tu organización, envíale una invitación.",
"info": "Puedes invitar múltiples miembros a la vez.",
"missingPermission": "Necesitas permisos adicionales para invitar miembros a esta organización.",
"success_one": "¡Miembro invitado exitosamente!",
"success_other": "¡Miembros invitados exitosamente!",
"email": {
"subject": "Has sido invitado a unirte a una organización",
"preview": "Únete a {{inviter}} en TurboStarter",
"body": "<bold>{{inviter}}</bold> te ha invitado a unirte a <bold>{{organization}}</bold> en TurboStarter.",
"or": "O, copia y pega este enlace en tu navegador:",
"cta": "Unirse a {{organization}}",
"disclaimer": "Si no esperabas esta invitación, no te preocupes, puedes ignorar este correo."
}
},
"update": {
"role": {
"title": "Actualizar el rol de {{name}}",
"description": "Cambia el rol del miembro seleccionado desde el menú desplegable.",
"info": "El rol determina los permisos del miembro.",
"success": "¡Rol del miembro actualizado exitosamente!"
}
},
"remove": { "success": "¡Miembro eliminado exitosamente!" }
},
"name": {
"edit": {
"description": "Ingresa el nombre de tu organización, o un nombre con el que te sientas cómodo.",
"success": "¡Nombre de la organización actualizado exitosamente!",
"info": "Usa como máximo 32 caracteres.",
"missingPermission": "Necesitas permisos adicionales para gestionar el nombre de tu organización."
}
},
"logo": {
"description": "Este es el logo de tu organización.\nHaz clic en el logo para subir uno personalizado desde tus archivos.",
"info": "Aunque no es obligatorio, se recomienda agregar un logo.",
"missingPermission": "Necesitas permisos adicionales para gestionar el logo de tu organización.",
"update": {
"success": "¡Logo de la organización actualizado exitosamente!"
},
"remove": {
"success": "¡Logo de la organización eliminado exitosamente!"
}
},
"leave": {
"title": "Salir de la organización",
"description": "Revoca tu acceso a esta organización. Cualquier recurso agregado permanecerá.",
"cta": "Salir de la organización",
"disclaimer": "¿Seguro que quieres salir de esta organización?\n\nYa no tendrás acceso a sus recursos y necesitarás ser invitado nuevamente para unirte.\n\n¿Deseas continuar?",
"success": "¡Has salido de la organización exitosamente!",
"cannotLeaveAsOnlyOwner": "Para salir, asegúrate de que al menos otro miembro tenga el rol de Propietario."
},
"delete": {
"title": "Eliminar organización",
"description": "Elimina permanentemente esta organización y todo su contenido de la plataforma TurboStarter. Esta acción no es reversible.",
"cta": "Eliminar organización",
"disclaimer": "Estás a punto de eliminar esta organización. \n\nDespués, todos los datos (incluidos miembros y equipos) serán eliminados permanentemente y no podrán recuperarse. \n\n¿Deseas continuar?",
"success": "¡Organización eliminada exitosamente!",
"missingPermission": "Necesitas permisos adicionales para eliminar esta organización."
},
"error": {
"slugAlreadyTaken": "El slug ya está tomado.",
"cannotCreateNew": "No puedes crear una nueva organización.",
"maximumNumberOfOrganizations": "Has alcanzado el número máximo de organizaciones.",
"alreadyExists": "Ya existe una organización con este nombre.",
"notFound": "No pudimos encontrar la organización.",
"userNotMember": "No eres miembro de esta organización.",
"cannotUpdate": "No puedes actualizar esta organización.",
"cannotDelete": "No puedes eliminar esta organización.",
"noActive": "No se encontró ninguna organización activa.",
"userAlreadyMember": "Ya eres miembro de esta organización.",
"memberNotFound": "No pudimos encontrar al miembro.",
"roleNotFound": "No pudimos encontrar el rol.",
"cannotLeaveAsOnlyOwner": "No puedes dejar la organización como el único propietario.",
"cannotLeaveWithoutOwner": "No puedes dejar la organización sin un propietario.",
"cannotDeleteMember": "No puedes eliminar a este miembro.",
"cannotUpdateMember": "No puedes actualizar a este miembro.",
"membershipLimitReached": "Has alcanzado el límite de miembros para esta organización.",
"cannotAccess": "No puedes acceder a esta organización.",
"slugNotAvailable": "El slug no está disponible.",
"team": {
"cannotCreateNew": "No puedes crear un nuevo equipo.",
"alreadyExists": "Ya existe un equipo con este nombre.",
"notFound": "No pudimos encontrar el equipo.",
"maximumNumberOfTeams": "Has alcanzado el número máximo de equipos.",
"unableToRemoveLastTeam": "No puedes eliminar el último equipo.",
"cannotCreate": "No puedes crear un equipo en esta organización.",
"cannotDelete": "No puedes eliminar un equipo en esta organización.",
"cannotUpdate": "No puedes actualizar un equipo en esta organización.",
"cannotAccessMembers": "No puedes acceder a los miembros de este equipo.",
"memberLimitReached": "Has alcanzado el límite de miembros para este equipo.",
"userNotMember": "No eres miembro de este equipo.",
"noActive": "No se encontró ningún equipo activo.",
"cannotCreateNewMember": "No puedes crear un nuevo miembro.",
"cannotRemoveMember": "No puedes eliminar a este miembro.",
"cannotAccess": "No puedes acceder a esta organización."
},
"invitation": {
"cannotInviteUsers": "No puedes invitar usuarios a esta organización.",
"userAlreadyInvited": "Este usuario ya está invitado a esta organización.",
"notFound": "No pudimos encontrar la invitación.",
"notRecipient": "No eres el destinatario de esta invitación.",
"cannotCancel": "No puedes cancelar esta invitación.",
"inviterNoLongerMember": "El invitador ya no es miembro de la organización.",
"cannotInviteUserWithRole": "No puedes invitar a un usuario con este rol.",
"emailVerificationRequired": "Se requiere verificación de correo antes de aceptar o rechazar una invitación.",
"failedToRetrieve": "No pudimos recuperar la invitación.",
"limitReached": "Has alcanzado el límite de invitaciones para esta organización."
},
"ac": {
"missingAcInstance": "Falta la instancia AC.",
"mustBeInOrganizationToCreateRole": "Debes estar en una organización para crear un rol.",
"cannotCreateRole": "No puedes crear un rol.",
"cannotUpdateRole": "No puedes actualizar un rol.",
"cannotDeleteRole": "No puedes eliminar un rol.",
"cannotReadRole": "No puedes leer un rol.",
"cannotListRole": "No puedes listar roles.",
"cannotGetRole": "No puedes obtener un rol.",
"tooManyRoles": "Has alcanzado el número máximo de roles.",
"invalidResource": "El recurso es inválido.",
"cannotDeletePreDefinedRole": "No puedes eliminar un rol predefinido.",
"roleNameAlreadyTaken": "El nombre del rol ya está en uso."
}
}
}

View File

@@ -0,0 +1,145 @@
{
"error": {
"default": "Valor de entrada inválido",
"type": "Se esperaba {{expected}}, pero se recibió {{received}}",
"type_with_path": "{{path}} debería ser {{expected}}, pero se recibió {{received}}",
"undefined": "{{path}} es requerido",
"date": "Formato de fecha inválido",
"custom": "Valor de entrada inválido",
"notMultipleOf": "El número debe ser múltiplo de {{multipleOf}}",
"invalidKey": "Clave inválida",
"invalidElement": "Elemento inválido: {{- origin}}",
"invalidValue_one": "Valor inválido: {{- values}}, se esperaba {{expected}}",
"invalidValue_other": "Valores inválidos: {{- values}}, se esperaba {{expected}}",
"string": {
"generic": "Formato inválido - {{format}}",
"email": "Por favor ingresa una dirección de correo válida",
"url": "Por favor ingresa una URL válida",
"uuid": "Por favor ingresa un UUID válido",
"cuid": "Por favor ingresa un CUID válido",
"regex": "Formato inválido - {{validation}}",
"datetime": "Formato inválido - {{validation}}",
"startsWith": "Debe comenzar con {{startsWith}}",
"endsWith": "Debe terminar con {{endsWith}}"
},
"file": {
"type": "Solo se aceptan archivos {{type}}.",
"maxCount_one": "Solo puedes subir {{count}} archivo.",
"maxCount_other": "Solo puedes subir hasta {{count}} archivos."
},
"tooSmall": {
"array": {
"inclusive": "Debe contener al menos {{minimum}} elemento(s)",
"inclusive_one": "Debe contener al menos {{minimum}} elemento",
"inclusive_other": "Debe contener al menos {{minimum}} elementos",
"notInclusive": "Debe contener más de {{minimum}} elemento(s)",
"notInclusive_one": "Debe contener más de {{minimum}} elemento",
"notInclusive_other": "Debe contener más de {{minimum}} elementos"
},
"string": {
"inclusive": "Debe contener al menos {{minimum}} caracter(es)",
"inclusive_one": "Debe contener al menos {{minimum}} caracter",
"inclusive_other": "Debe contener al menos {{minimum}} caracteres",
"inclusive_with_path": "{{path}} debe contener al menos {{minimum}} caracter(es)",
"inclusive_with_path_one": "{{path}} debe contener al menos {{minimum}} caracter",
"inclusive_with_path_other": "{{path}} debe contener al menos {{minimum}} caracteres",
"notInclusive": "Debe contener más de {{minimum}} caracter(es)",
"notInclusive_one": "Debe contener más de {{minimum}} caracter",
"notInclusive_other": "Debe contener más de {{minimum}} caracteres",
"notInclusive_with_path": "{{path}} debe contener más de {{minimum}} caracter(es)",
"notInclusive_with_path_one": "{{path}} debe contener más de {{minimum}} caracter",
"notInclusive_with_path_other": "{{path}} debe contener más de {{minimum}} caracteres"
},
"number": {
"inclusive": "Debe ser mayor o igual a {{minimum}}",
"inclusive_with_path": "{{path}} debe ser mayor o igual a {{minimum}}",
"notInclusive": "Debe ser mayor que {{minimum}}",
"notInclusive_with_path": "{{path}} debe ser mayor que {{minimum}}"
},
"set": {
"inclusive": "Valor de entrada inválido",
"notInclusive": "Valor de entrada inválido"
},
"date": {
"inclusive": "La fecha debe ser igual o posterior a {{- minimum, datetime}}",
"notInclusive": "La fecha debe ser posterior a {{- minimum, datetime}}"
},
"file": {
"inclusive": "El tamaño del archivo debe ser menor a {{maximum}}MB",
"notInclusive": "El tamaño del archivo debe ser menor a {{maximum}}MB"
}
},
"tooBig": {
"array": {
"inclusive": "Debe contener como máximo {{maximum}} elemento(s)",
"inclusive_one": "Debe contener como máximo {{maximum}} elemento",
"inclusive_other": "Debe contener como máximo {{maximum}} elementos",
"notInclusive": "Debe contener menos de {{maximum}} elemento(s)",
"notInclusive_one": "Debe contener menos de {{maximum}} elemento",
"notInclusive_other": "Debe contener menos de {{maximum}} elementos"
},
"string": {
"inclusive": "Debe contener como máximo {{maximum}} caracter(es)",
"inclusive_one": "Debe contener como máximo {{maximum}} caracter",
"inclusive_other": "Debe contener como máximo {{maximum}} caracteres",
"inclusive_with_path": "{{path}} no debe exceder {{maximum}} caracter(es)",
"inclusive_with_path_one": "{{path}} no debe exceder {{maximum}} caracter",
"inclusive_with_path_other": "{{path}} no debe exceder {{maximum}} caracteres",
"notInclusive": "Debe contener menos de {{maximum}} caracter(es)",
"notInclusive_one": "Debe contener menos de {{maximum}} caracter",
"notInclusive_other": "Debe contener menos de {{maximum}} caracteres",
"notInclusive_with_path": "{{path}} debe contener menos de {{maximum}} caracter(es)",
"notInclusive_with_path_one": "{{path}} debe contener menos de {{maximum}} caracter",
"notInclusive_with_path_other": "{{path}} debe contener menos de {{maximum}} caracteres"
},
"number": {
"inclusive": "Debe ser menor o igual a {{maximum}}",
"inclusive_with_path": "{{path}} debe ser menor o igual a {{maximum}}",
"notInclusive": "Debe ser menor que {{maximum}}",
"notInclusive_with_path": "{{path}} debe ser menor que {{maximum}}"
},
"set": {
"inclusive": "Valor de entrada inválido",
"notInclusive": "Valor de entrada inválido"
},
"date": {
"inclusive": "La fecha debe ser igual o anterior a {{- maximum, datetime}}",
"notInclusive": "La fecha debe ser anterior a {{- maximum, datetime}}"
},
"file": {
"inclusive": "El tamaño del archivo debe ser menor a {{maximum}}MB",
"notInclusive": "El tamaño del archivo debe ser menor a {{maximum}}MB"
}
}
},
"validation": {
"email": "correo electrónico",
"url": "url",
"uuid": "uuid",
"cuid": "cuid",
"regex": "expresión regular",
"datetime": "fecha y hora"
},
"type": {
"function": "función",
"number": "número",
"string": "texto",
"nan": "NaN",
"integer": "entero",
"float": "decimal",
"boolean": "booleano",
"date": "fecha",
"bigint": "bigint",
"undefined": "indefinido",
"symbol": "símbolo",
"null": "nulo",
"array": "array",
"object": "objeto",
"unknown": "desconocido",
"promise": "promesa",
"void": "vacío",
"never": "nunca",
"map": "mapa",
"set": "conjunto"
}
}

View File

@@ -0,0 +1,12 @@
import { en } from "./en";
import { es } from "./es";
import type { config } from "../config";
export const translations: Record<
(typeof config.locales)[number],
typeof en & typeof es
> = {
en,
es,
} as const;

View File

@@ -0,0 +1,13 @@
export const Locale = {
EN: "en",
ES: "es",
} as const;
export type Locale = (typeof Locale)[keyof typeof Locale];
export const LocaleLabel: Record<Locale, string> = {
[Locale.EN]: "English",
[Locale.ES]: "Español",
} as const;
export type { TFunction } from "i18next";

21
packages/i18n/src/typings/i18n.d.ts vendored Normal file
View File

@@ -0,0 +1,21 @@
import "i18next";
import type { config } from "../config";
import type { translations } from "../translations";
type ExtractDefault<T> = T extends () => Promise<infer R>
? R extends { default: infer D }
? D
: never
: never;
type Translation = (typeof translations)[keyof typeof translations];
declare module "i18next" {
interface CustomTypeOptions {
defaultNS: typeof config.namespaces;
resources: {
[K in keyof Translation]: ExtractDefault<Translation[K]>;
};
}
}

View File

@@ -0,0 +1,57 @@
import { logger } from "@turbostarter/shared/logger";
import { config } from "../config";
import { translations } from "../translations";
import type { TranslationKey } from "../client";
import type { DefaultNamespace, Namespace, i18n } from "i18next";
export const loadTranslation = async (
locale: (typeof config.locales)[number],
namespace: (typeof config.namespaces)[number],
) => {
try {
const data = await translations[locale][namespace]();
return data.default;
} catch {
logger.error(`Error while loading i18n file: ${locale}/${namespace}.json`);
return {};
}
};
export const isKey = <T extends Namespace = DefaultNamespace>(
key: string,
i18n?: i18n,
ns?: T,
): key is TranslationKey<T> => {
return i18n?.exists(key, { ns }) ?? false;
};
export const isLocaleSupported = (
locale: string,
): locale is (typeof config.locales)[number] => config.locales.includes(locale);
export const getPathname = ({
locale,
path,
defaultLocale,
}: {
locale: string;
path: string;
defaultLocale?: string;
}) => {
const pathname = path.replace(
new RegExp(`^/(${config.locales.join("|")})`),
"",
);
if (locale === (defaultLocale ?? config.defaultLocale)) {
return pathname.startsWith("/") ? pathname : `/${pathname}`;
}
return `/${locale}${pathname.startsWith("/") ? pathname : `/${pathname}`}`;
};
export * from "./validation";

View File

@@ -0,0 +1,378 @@
import i18next from "i18next";
import en from "zod/v4/locales/en.js";
import { config } from "../config";
import type { TFunction } from "i18next";
import type { $ZodIssue, $ZodIssueBase, $ZodRawIssue } from "zod/v4/core";
const defaultErrorMap = en().localeError;
const jsonStringifyReplacer = (_: string, value: unknown): unknown => {
if (typeof value === "bigint") {
return value.toString();
}
return value;
};
function joinValues<T extends unknown[]>(array: T, separator = " | "): string {
return array
.map((val) => (typeof val === "string" ? `'${val}'` : val))
.join(separator);
}
const isRecord = (value: unknown): value is Record<string, unknown> => {
if (typeof value !== "object" || value === null) return false;
for (const key in value) {
if (!Object.prototype.hasOwnProperty.call(value, key)) return false;
}
return true;
};
const getKeyAndValues = (
param: unknown,
defaultKey: string,
): {
values: Record<string, unknown>;
key: string;
} => {
if (typeof param === "string") return { key: param, values: {} };
if (isRecord(param)) {
const key =
"key" in param && typeof param.key === "string" ? param.key : defaultKey;
const values =
"values" in param && isRecord(param.values) ? param.values : {};
return { key, values };
}
return { key: defaultKey, values: {} };
};
const parsedType = (data: unknown): string => {
const t = typeof data;
switch (t) {
case "number": {
return Number.isNaN(data) ? "NaN" : "number";
}
case "object": {
if (Array.isArray(data)) {
return "array";
}
if (data === null) {
return "null";
}
if (
Object.getPrototypeOf(data) !== Object.prototype &&
data?.constructor
) {
return data.constructor.name;
}
}
}
return t;
};
export type $ZodErrorMap<T extends $ZodIssueBase = $ZodIssue> = (
issue: $ZodRawIssue<T>,
) => {
message: string;
code?: string;
};
export type MakeZodI18nMap = (options?: ZodI18nMapOptions) => $ZodErrorMap;
export interface ZodI18nMapOptions {
t?: TFunction;
handlePath?: HandlePathOption | false;
}
export interface HandlePathOption {
context?: string;
keyPrefix?: string;
}
const defaultNs = "validation";
export const makeZodI18nMap: MakeZodI18nMap = (options) => (issue) => {
const { t, ns, handlePath } = {
t: i18next.t,
ns: defaultNs,
...options,
handlePath:
options?.handlePath !== false
? {
context: "with_path",
ns: config.namespaces,
keyPrefix: undefined,
...options?.handlePath,
}
: null,
} as const;
const defaultResult = defaultErrorMap(issue);
const defaultMessage =
typeof defaultResult === "string"
? defaultResult
: (defaultResult?.message ?? "");
const path =
issue.path && issue.path.length > 0 && !!handlePath
? {
context: handlePath.context,
path: t(
[handlePath.keyPrefix, issue.path.join(".")]
.filter(Boolean)
.join("."),
{
ns: handlePath.ns,
defaultValue: issue.path.join("."),
},
),
}
: {};
switch (issue.code) {
case "invalid_type":
if (issue.received === undefined || issue.received === null) {
const code = `${ns}:error.undefined`;
return {
message: t(code, {
ns,
defaultValue: defaultMessage,
...path,
}),
code,
};
} else {
const parsed = parsedType(issue.input).toLocaleLowerCase();
const code = `${ns}:error.type`;
return {
message: t(code, {
ns,
expected: t(`type.${issue.expected}`, {
defaultValue: issue.expected,
}),
received: t(`type.${parsed}`, {
defaultValue: parsed,
}),
defaultValue: defaultMessage,
...path,
}),
code,
};
}
case "unrecognized_keys": {
const code = `${ns}:error.unrecognizedKeys`;
return {
message: t(code, {
keys: joinValues(issue.keys, ", "),
count: issue.keys.length,
ns,
defaultValue: defaultMessage,
...path,
}),
code,
};
}
case "invalid_union": {
const code = `${ns}:error.union`;
return {
message: t(code, {
ns,
defaultValue: defaultMessage,
...path,
}),
code,
};
}
case "invalid_key": {
const code = `${ns}:error.invalidKey`;
return {
message: t(code, {
ns,
defaultValue: defaultMessage,
...path,
}),
code,
};
}
case "invalid_element": {
const code = `${ns}:error.invalidElement`;
return {
message: t(code, {
origin: issue.origin,
ns,
defaultValue: defaultMessage,
...path,
}),
code,
};
}
case "invalid_value": {
const code = `${ns}:error.invalidValue`;
return {
message: t(code, {
values: joinValues(issue.values, ", "),
count: issue.values.length,
expected: JSON.stringify(issue.values, jsonStringifyReplacer),
ns,
defaultValue: defaultMessage,
...path,
}),
code,
};
}
case "too_small": {
const minimum =
issue.origin === "date"
? new Date(issue.minimum as number)
: issue.minimum;
const code = `${ns}:error.tooSmall.${issue.origin}.${
issue.exact ? "exact" : issue.inclusive ? "inclusive" : "notInclusive"
}`;
return {
message: t(code, {
minimum,
count: typeof minimum === "number" ? minimum : undefined,
ns,
defaultValue: defaultMessage,
...path,
}),
code,
};
}
case "too_big": {
const maximum =
issue.origin === "date"
? new Date(issue.maximum as number)
: issue.maximum;
const code = `${ns}:error.tooBig.${issue.origin}.${
issue.exact ? "exact" : issue.inclusive ? "inclusive" : "notInclusive"
}`;
return {
message: t(code, {
maximum,
count: typeof maximum === "number" ? maximum : undefined,
ns,
defaultValue: defaultMessage,
...path,
}),
code,
};
}
case "invalid_format": {
if (issue.format === "starts_with") {
const code = `${ns}:error.string.startsWith`;
return {
message: t(code, {
startsWith: issue.prefix,
ns,
defaultValue: defaultMessage,
...path,
}),
code,
};
} else if (issue.format === "ends_with") {
const code = `${ns}:error.string.endsWith`;
return {
message: t(code, {
endsWith: issue.suffix,
ns,
defaultValue: defaultMessage,
...path,
}),
code,
};
} else if (issue.format === "includes") {
const code = `${ns}:error.string.includes`;
return {
message: t(code, {
includes: issue.includes,
ns,
defaultValue: defaultMessage,
...path,
}),
};
} else if (issue.format === "regex") {
const code = `${ns}:error.string.regex`;
return {
message: t(code, {
pattern: issue.pattern,
ns,
defaultValue: defaultMessage,
...path,
}),
code,
};
} else {
const code = `${ns}:error.string.generic`;
return {
message: t(code, {
format: issue.format,
ns,
defaultValue: defaultMessage,
...path,
}),
code,
};
}
}
case "custom": {
const { key, values } = getKeyAndValues(
issue.params?.i18n,
"error.custom",
);
return {
message: t(key, {
...values,
ns,
defaultValue: defaultMessage,
...path,
}),
code: key,
};
}
case "not_multiple_of": {
const code = `${ns}:error.notMultipleOf`;
return {
message: t(code, {
multipleOf: issue.multipleOf,
ns,
defaultValue: defaultMessage,
...path,
}),
code,
};
}
default:
return { message: defaultMessage, code: `${ns}:error.default` };
}
};