Esc

glosa

Add the Vite plugin, set up the runtime for Vue, React or Svelte, and switch the locale.

pnpm add glosa

glosa is in private preview and isn't on npm yet. Until it is, build it from the repository with pnpm install and pnpm build.

Add glosa() next to your framework's plugin. It runs first either way, so it rewrites each component before the framework compiles it.

Vue
import vue from "@vitejs/plugin-vue";
import { glosa } from "glosa/vite";
import { defineConfig } from "vite";

export default defineConfig({
  plugins: [glosa(), vue()],
});
ts
React
import react from "@vitejs/plugin-react";
import { glosa } from "glosa/vite";
import { defineConfig } from "vite";

export default defineConfig({
  plugins: [glosa(), react()],
});
ts
Svelte
import { svelte } from "@sveltejs/vite-plugin-svelte";
import { glosa } from "glosa/vite";
import { defineConfig } from "vite";

export default defineConfig({
  plugins: [glosa(), svelte()],
});
ts

It reads .vue, .jsx, .tsx and .svelte files. When the dev server or a build starts, it writes locales/en.json with every message, and in dev it updates the file on every save. Two options change where: sourceLocale, the language you write in ("en"), and dir, the catalog folder from the Vite root ("locales").

virtual:glosa holds the source locale and a loader for each catalog in locales/. Pass it to createGlosa, then give the store to your app.

Vue
// main.ts
import config from "virtual:glosa";
import { createGlosa, glosaVue } from "glosa/vue";
import { createApp } from "vue";
import App from "./App.vue";

const glosa = createGlosa(config);
createApp(App).use(glosaVue(glosa)).mount("#app");
ts
React
// main.tsx
import config from "virtual:glosa";
import { createGlosa, GlosaProvider } from "glosa/react";
import { createRoot } from "react-dom/client";
import App from "./App.tsx";

export const glosa = createGlosa(config);

createRoot(document.getElementById("app")!).render(
  <GlosaProvider glosa={glosa}>
    <App />
  </GlosaProvider>,
);
tsx
Svelte
<!-- App.svelte, the root component -->
<script lang="ts">
  import config from "virtual:glosa";
  import { createGlosa, setGlosa } from "glosa/svelte";

  const glosa = setGlosa(createGlosa(config));
</script>
svelte

Now start the dev server and open locales/en.json. Copy it to locales/es.json and translate the values. A message with no translation shows its source text.

setLocale loads the locale's catalog the first time, then switches. Every message on the page re-renders, without a reload. glosa.locales lists every locale with a catalog, the source locale first.

Vue
<script setup lang="ts">
import type { Glosa } from "glosa/vue";
import { inject } from "vue";

const glosa = inject<Glosa>("glosa")!;
</script>

<template>
  <button v-for="locale in glosa.locales" :key="locale" @click="glosa.setLocale(locale)">
    {{ locale }}
  </button>
</template>
vue
React
import { glosa } from "./main.tsx";

export function Locales() {
  return glosa.locales.map((locale) => (
    <button key={locale} onClick={() => glosa.setLocale(locale)}>
      {locale}
    </button>
  ));
}
tsx
Svelte
<!-- In App.svelte, under the setGlosa call -->
{#each glosa.locales as locale}
  <button onclick={() => glosa.setLocale(locale)}>{locale}</button>
{/each}
svelte

To start in another locale, pass it as locale. Its catalog loads right away, and await glosa.ready waits for it, which a server render should do.

Add glosa's types for virtual:glosa to tsconfig.json:

{ "compilerOptions": { "types": ["glosa/client"] } }
json