---
order: 2
section: guides
title: Configuración
description: >-
  Todas las opciones de scribe.config.ts: ajustes del proyecto, tipos de
  contenido, marcadores de campo y enrutamiento por idioma.
noindex: false
canonicalPath: /es/docs/configuration
---
Todo se centraliza en un único archivo, `scribe.config.ts`, que exporta `defineConfig({...})`. Tanto el entorno de ejecución (`createScribe`) como la CLI (que detecta `scribe.config.ts` en el directorio de trabajo) leen este mismo archivo.

## Opciones del proyecto

```ts
export default defineConfig({
  rootDir: ".",
  contentDir: "content",             // default
  store: ".scribe/store.sqlite",     // default
  assetsDir: "public",               // optional
  locales: ["en", "fr", "de"],
  defaultLocale: "en",               // default
  localeRouting: { strategy: "path-prefix", prefixDefaultLocale: false },
  localeFallbacks: true,             // default
  localePresets: { active: ["fr"] },
  translate: { /* see the translation page */ },
  types: [ /* defineContentType(...) */ ],
});
```

| Opción | Predeterminado | Descripción |
| --- | --- | --- |
| `rootDir` | Requerido | Directorio raíz del proyecto. Déjalo como ruta relativa (normalmente `"."`): la CLI lo resuelve respecto a la carpeta del archivo de configuración y `createScribe` respecto a `process.cwd()`. No lo construyas a partir de `import.meta.url`: los empaquetadores insertan esa ruta durante la compilación, lo que causa errores en entornos sin servidor como Vercel. |
| `contentDir` | `"content"` | Directorio que contiene una carpeta por cada tipo de contenido. |
| `store` | `".scribe/store.sqlite"` | Almacén de traducciones SQLite. **Añádelo a tu repositorio de control de versiones; no incluyas `.scribe/` en tu gitignore.** |
| `assetsDir` | Opcional | Raíz de los archivos estáticos (por ejemplo, `"public"`). Al configurarlo, `scribe validate` avisa si hay rutas de imágenes en el encabezado o en los cuerpos que no existen en el disco. |
| `locales` | Requerido | Todos los idiomas, incluido el predeterminado. |
| `defaultLocale` | `"en"` | Idioma de origen canónico. Debe aparecer obligatoriamente en `locales`. |
| `localeRouting` | path-prefix, unprefixed default | Define cómo aparecen los marcadores de idioma en las URL generadas. `path-prefix` añade el idioma al principio (omitiendo el predeterminado a menos que `prefixDefaultLocale` sea true). `search-param` añade `?param=locale` para los idiomas no predeterminados. |
| `localeFallbacks` | `true` | Genera cadenas de respaldo a partir de las etiquetas de idioma. Si una variante regional no tiene traducción para un documento, se muestra la traducción más cercana en el idioma base (por ejemplo, `pt-BR` recurre a `pt`) antes de pasar al idioma predeterminado. Configúralo en `false` para desactivarlo. Consulta la sección [Cadenas de respaldo de idioma](#locale-fallback-chains). |
| `localePresets` | Opcional | Grupos de idiomas con nombre para usar con `scribe translate --preset name`. |
| `translate` | Opcional | Valores predeterminados de traducción para todo el proyecto ([traducción](/docs/translation)). |
| `types` | Requerido | Definiciones de los tipos de contenido. |

Las configuraciones se validan desde el primer momento: un `defaultLocale` desconocido, identificadores de tipo duplicados o una plantilla de `path` mal formada provocarán un error inmediato con un mensaje claro.

## Opciones de tipo de contenido

```ts
defineContentType({
  id: "blog",
  schema: blogSchema,
  path: "/blog/{slug}",
  contentDir: "blog",          // default: the id
  slugStrategy: "localized",   // default: "fixed"
  indexFallback: "en",         // default: "en" if path is set, else "none"
  orderBy: "-publishedAt",     // default: "slug"
  label: "Blog",               // default: capitalized id (studio UI only)
  crossValidate: (data, ctx) => [],
  translate: { /* per-type overrides */ },
})
```

| Opción | Descripción |
| --- | --- |
| `id` | Identificador único. Se convierte en el método de acceso tipado (`scribe.blog`) y en el `contentDir` predeterminado. |
| `schema` | Esquema de objetos Zod para el encabezado (frontmatter), con sus respectivos marcadores de campo (ver más abajo). |
| `path` | Plantilla de URL con exactamente un `{slug}` (por ejemplo, `/blog/{slug}`). Omítela para tipos que sean **solo de referencia** (autores, categorías) y que no tengan páginas propias. |
| `contentDir` | Carpeta dentro del `contentDir` del proyecto que alberga los archivos `.mdx` de este tipo. |
| `slugStrategy` | `"fixed"`: todos los idiomas usan el slug en inglés. `"localized"`: el traductor genera un slug específico para cada idioma (`/es/blog/hola-mundo`). |
| `indexFallback` | Lo que devuelve `resolve()` cuando un idioma no tiene traducción: `"en"` sirve el documento en inglés (con `actualLocale: "en"`), mientras que `"none"` no devuelve nada. |
| `orderBy` | Orden predeterminado para `list()`: `"slug"`, `"publishedAt"`, `"-publishedAt"`, `"updatedAt"`, `"-updatedAt"`, o un comparador `(a, b) => number` sobre documentos con tipado completo. |
| `crossValidate` | Validación adicional ejecutada por `scribe validate`, que recibe el encabezado analizado (tipado). Devuelve `{ field, message, level }[]`. |
| `translate` | Indicaciones, reglas o modelos de traducción específicos para cada tipo ([traducción](/docs/translation)). |

## Marcadores de campo

Cada campo del esquema pertenece a uno de tres tipos. Los campos sin marcar son **estructurales** por defecto.

```ts
import { field } from "scribe-cms";

const schema = z.object({
  // Se envía al traductor y se guarda por idioma:
  title: field.translatable(z.string().min(1)),

  // Solo en inglés; se copia del documento en inglés a todos los idiomas:
  heroImage: field.structural(z.string().optional()),

  // Referencia(s) del slug en inglés a otro tipo de contenido:
  author: field.relation("author"),
  relatedPosts: field.relation("blog", { multiple: true, max: 4, optional: true }),
});
```

Los marcadores traducibles también funcionan en campos anidados dentro de matrices o arreglos sencillos (por ejemplo, `sections.*.title`). Simplemente asegúrate de no envolver el arreglo contenedor en un `field.structural` o todo el subárbol quedará únicamente en inglés.

### `field.relation(typeId, options?)`

| Opción | Predeterminado | Descripción |
| --- | --- | --- |
| `multiple` | `false` | El campo es un arreglo de slugs. |
| `optional` | `false` | Se puede omitir el campo. `related()` devolverá `null` (si es único) o ignorará los elementos que falten (si es múltiple). |
| `min` / `max` | Opcional | Límites en la cantidad de elementos (solo para `multiple: true`). |

Las restricciones van dentro del objeto de opciones, **no** en los métodos encadenados de Zod. Encadenar métodos (`.max(8)`, `.optional()`) clona el esquema y eliminaría los metadatos de la relación.

Las relaciones siempre guardan **slugs en inglés** y se verifican mediante `scribe validate` (una relación requerida huérfana bloquea la compilación con un error, mientras que una opcional solo genera una advertencia). Además, se resuelven en tiempo de ejecución con `related()`. Consulta la [API de tiempo de ejecución](/docs/runtime-api).

## Cadenas de respaldo de idioma

Por defecto, si un idioma regional no tiene traducción para un documento, se muestra su idioma base antes de pasar al idioma predeterminado. Cada idioma prueba prefijos cada vez más cortos de su propia etiqueta que también estén presentes en `locales`. Así, `fr-CA` recurre a `fr`, y `zh-Hant-TW` prueba primero con `zh-Hant` y luego con `zh`. Configúralo en `localeFallbacks: false` para desactivarlo. El idioma predeterminado nunca forma parte de esta cadena; sigue siendo el último recurso absoluto y se rige por el `indexFallback` del tipo de contenido.

Estas cadenas de respaldo se aplican a `resolve()` (el idioma servido se indica en `actualLocale` y las redirecciones de slug usan el slug del idioma de respaldo) y a `staticParams()` (los slugs pre-renderizados coinciden con lo que sirve `resolve()`). De forma intencionada, **no** se aplican a `get()`, `list()`, `translation()`, `alternates()` ni al mapa del sitio (sitemap), que requieren coincidencias exactas.

## Tipos de cliente con tipado

Para los alias de tipo en el lado de la aplicación, puedes derivar todo desde la configuración:

```ts
import type { ScribeClient, ScribeDocs } from "scribe-cms/runtime";
import config from "./scribe.config";

type MyScribe = ScribeClient<typeof config>;
type BlogDoc = ScribeDocs<typeof config>["blog"];
```
