Skip to content

i18n

Framework-Agnostic

The i18n key conventions and domain translations described on this page are framework-agnostic. The same translation structure works in React Native, React Web, Vue, and Svelte. Only the configureI18n import differs between framework packages.

Your schema is defined, the base i18n is already working — action buttons, table UI, dialogs, and validation messages are all translated. Now it's time to add labels for the fields and groups you just created.

Domain Translations

Create a locale file with labels matching your schema's field names and group names:

typescript
// src/lib/settings/locales/pt-BR.ts
export const ptBR = {
  product: {
    title: 'Product',
    fields: {
      id: 'ID',
      name: 'Name',
      sku: 'SKU',
      email: 'Email',
      active: 'Active',
      quantity: 'Quantity',
      price: 'Price',
    },
    groups: {
      info: 'Information',
      pricing: 'Pricing',
    },
  },
}

Each key maps directly to what you defined in the schema — name here is the name: text() field, info is the info: group() group.

Configuring i18n

Unlike React and Vue which use i18next or vue-i18n, @ybyra/sveltekit takes a custom translate function — you bring your own i18n library or write a simple resolver:

Step 1 — Translation Functions

typescript
// src/lib/settings/i18n.ts
import { ptBR } from '@ybyra/core'
import { ptBR as local } from './locales/pt-BR'

const messages = { ...ptBR, ...local } as Record<string, unknown>

function walk(obj: Record<string, unknown>, path: string): string | undefined {
  const parts = path.split('.')
  let current: unknown = obj
  for (const part of parts) {
    if (current == null || typeof current !== 'object') return undefined
    current = (current as Record<string, unknown>)[part]
  }
  return typeof current === 'string' ? current : undefined
}

function interpolate(template: string, params: Record<string, unknown>): string {
  return template.replace(/\{\{(\w+)\}\}/g, (_, key) => String(params[key] ?? ''))
}

export function translate(key: string, params?: Record<string, unknown>): string {
  const value = walk(messages, key)
  if (value === undefined) return key
  return params ? interpolate(value, params) : value
}

export function hasTranslation(key: string): boolean {
  return walk(messages, key) !== undefined
}

Step 2 — Register with Ybyra

typescript
// src/lib/settings/i18n-setup.ts
import { configureI18n } from '@ybyra/sveltekit'
import { translate, hasTranslation } from './i18n'

configureI18n({ translate, hasTranslation })

Import i18n-setup.ts in your root layout so it runs before any Ybyra components mount:

svelte
<!-- src/routes/+layout.svelte -->
<script lang="ts">
  import '$lib/settings/i18n-setup'
</script>

<slot />

What the Base Provides

The ptBR export from @ybyra/core includes two namespaces:

common.* — UI chrome shared across all domains:

KeyExample
common.actions.add"Adicionar"
common.actions.create"Criar"
common.actions.create.success"Registro criado com sucesso"
common.actions.destroy.confirm"Deseja realmente excluir este registro?"
common.table.empty"Nenhum registro encontrado"
common.table.page"Página de "
common.dialog.confirm"Confirmar"
common.scopes.index"Listagem"

validation.* — validation error messages:

KeyExample
validation.required"Campo obrigatório"
validation.minLength"Mínimo de caracteres"
validation.min"Valor mínimo é "
validation.pattern"Formato inválido"

You don't need to write any of these — they come ready with @ybyra/core.

Key Conventions

Ybyra resolves labels automatically using this convention:

PatternResolves toExample
{domain}.titlePage titleproduct.title → "Product"
{domain}.fields.{field}Field labelproduct.fields.name → "Name"
{domain}.fields.{field}[{state}]Field label for a stateproduct.fields.email[error] → "Invalid email"
{domain}.groups.{group}Group titleproduct.groups.info → "Information"
common.actions.{action}Action button labelcommon.actions.add → "Adicionar"
common.scopes.{scope}Scope name (used in page titles)common.scopes.add → "Cadastro"

You never hardcode labels in your schema — define the fields and groups, then create the matching translation keys.

Next Steps

  • Screens — build the 4 CRUD screens

Released under the MIT License.