Skip to content

lucasmotta/hablas

Repository files navigation

hablas

hablas is a Vite-first i18n package that combines:

  • extraction/sync plugin (hablas/vite)
  • virtual runtime modules (virtual:i18n + framework adapters)
  • ICU-aware helpers for plural and select (t.plural, t.select)

It is designed for fast DX during development: add messages in code, keep locale files synced, and consume translations through a single runtime API.

Why hablas

  • Vite-native virtual modules for app consumption.
  • Deterministic message keys based on source content hash.
  • Locale file sync on startup and watch.
  • Supports ICU plural/select flows and exact matches (=0, =1, ...).
  • Optional AI autofill for empty locale entries.
  • Framework adapters for React, React Signals, Preact, Vue, Svelte, and Solid.

Install

npm i hablas
# or
pnpm add hablas
# or
bun add hablas

Basic Setup

// vite.config.ts
import { defineConfig } from "vite"
import react from "@vitejs/plugin-react"
import { extractedI18nPlugin } from "hablas/vite"

export default defineConfig({
  plugins: [
    react(),
    extractedI18nPlugin({
      localesDir: "locales",
      sourceLocale: "en",
      defaultLocale: "en",
      include: ["src/**/*.{ts,tsx}"],
      removeUnused: true,
      verbose: true,
    }),
  ],
})

Runtime Usage

import { t } from "virtual:i18n"

t("Hello {name}", { name: "Lucas" })

t.plural(
  "count",
  {
    "=0": "Your cart is empty.",
    one: "You have {count} item.",
    other: "You have {count} items.",
  },
  { count: 3 }
)

t.select(
  "role",
  {
    admin: "Welcome back, admin.",
    other: "Welcome back.",
  },
  { role: "admin" }
)

await t.setLocale("fr")

For TypeScript, add a paths mapping (TypeScript does not resolve virtual:* specifiers by default):

// tsconfig.json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "virtual:i18n": ["./node_modules/hablas/types/virtual-i18n.d.ts"],
      "virtual:i18n-react": ["./node_modules/hablas/types/virtual-i18n-react.d.ts"],
      "virtual:i18n-react-signals": [
        "./node_modules/hablas/types/virtual-i18n-react-signals.d.ts"
      ],
      "virtual:i18n-preact": ["./node_modules/hablas/types/virtual-i18n.d.ts"],
      "virtual:i18n-vue": ["./node_modules/hablas/types/virtual-i18n.d.ts"],
      "virtual:i18n-svelte": ["./node_modules/hablas/types/virtual-i18n.d.ts"],
      "virtual:i18n-solid": ["./node_modules/hablas/types/virtual-i18n.d.ts"]
    }
  }
}

And include:

/// <reference types="hablas" />

Adapter imports

  • virtual:i18n-react
  • virtual:i18n-react-signals
  • virtual:i18n-preact
  • virtual:i18n-vue
  • virtual:i18n-svelte
  • virtual:i18n-solid

Auto-Translate (optional)

extractedI18nPlugin({
  // ...
  autoTranslate: {
    aiGatewayApiKey: process.env.AI_GATEWAY_API_KEY!,
    aiModel: process.env.AI_MODEL!,
  },
})

Notes:

  • Skipped when CI=true or CI=1.
  • Only fills missing/empty locale values.
  • Preserves placeholders and ICU structures.

Development

npm run build
npm run lint
npm run format
npm run test

Examples

Three Vite examples are included in examples/:

  • examples/react-hook
  • examples/react-signals
  • examples/svelte

Each example uses the same UI/content with locales:

  • en
  • fr
  • pt-BR
  • ru-RU

Run one example:

cd examples/react-hook
npm install
npm run dev

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors