npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@devwizard/laravel-localizer-vue

v1.0.1

Published

Vue 3 integration for Laravel Localizer with Vite plugin, useLocalizer composable, and automatic TypeScript generation

Downloads

30

Readme

@devwizard/laravel-localizer-vue

npm version npm downloads License: MIT

Vue 3 integration for Laravel Localizer - seamlessly use Laravel translations in your Vue 3/Inertia.js applications with full TypeScript support.

Features

  • 🎨 Vue 3 Composable - useLocalizer() composable with Composition API
  • 🔌 Vite Plugin - Auto-regenerates TypeScript translations on file changes
  • 🎯 TypeScript - Full type safety with TypeScript support
  • Inertia.js - Native integration with Inertia.js page props
  • 🌐 Pluralization - Built-in pluralization support
  • 🔄 Replacements - Dynamic placeholder replacement
  • 🌍 RTL Support - Automatic text direction detection
  • ⚛️ Reactive - Fully reactive locale and direction with Vue refs
  • 📦 Tree-shakeable - Modern ESM build

Requirements

  • Vue 3.0+
  • Inertia.js v1 or v2
  • Laravel Localizer backend package

Installation

npm install @devwizard/laravel-localizer-vue

Backend Setup

First, install and configure the Laravel Localizer package:

composer require devwizardhq/laravel-localizer
php artisan localizer:install

See the Laravel Localizer documentation for complete backend setup.

Setup

Step 1: Generate Translation Files

First, generate TypeScript translation files from your Laravel app:

php artisan localizer:generate --all

This creates files like resources/js/lang/en.ts, resources/js/lang/fr.ts, etc.

Step 2: Configure Vite Plugin

Add the Vite plugin to auto-regenerate translations when language files change.

File: vite.config.ts

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import laravel from 'laravel-vite-plugin';
import { laravelLocalizer } from '@devwizard/laravel-localizer-vue/vite';

export default defineConfig({
  plugins: [
    laravel({
      input: ['resources/js/app.ts'],
      refresh: true,
    }),
    vue({
      template: {
        transformAssetUrls: {
          base: null,
          includeAbsolute: false,
        },
      },
    }),
    laravelLocalizer({
      // Watch patterns for language file changes
      patterns: ['lang/**', 'resources/lang/**'],

      // Command to run when files change
      command: 'php artisan localizer:generate --all',

      // Enable debug logging (optional)
      debug: false,
    }),
  ],
});

What it does:

  • Watches for changes in lang/** and resources/lang/**
  • Automatically runs php artisan localizer:generate --all when files change
  • Triggers HMR to reload your frontend with updated translations

Step 3: Initialize Window Translations

Set up the global window.localizer object in your app entry point.

File: resources/js/app.ts

import './bootstrap';
import '../css/app.css';

import { createApp, h, DefineComponent } from 'vue';
import { createInertiaApp } from '@inertiajs/vue3';
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';

// Import all generated translation files
import * as translations from './lang';

const appName = import.meta.env.VITE_APP_NAME || 'Laravel';

createInertiaApp({
  title: (title) => `${title} - ${appName}`,
  resolve: (name) =>
    resolvePageComponent(
      `./Pages/${name}.vue`,
      import.meta.glob<DefineComponent>('./Pages/**/*.vue')
    ),
  setup({ el, App, props, plugin }) {
    // Initialize window.localizer with translations
    if (typeof window !== 'undefined') {
      window.localizer = {
        translations,
      };
    }

    createApp({ render: () => h(App, props) })
      .use(plugin)
      .mount(el);
  },
  progress: {
    color: '#4B5563',
  },
});

TypeScript Declaration

To ensure type safety when accessing window.localizer, add this global declaration to your project:

declare global {
  interface Window {
    localizer: {
      translations: typeof translations;
    };
  }
}

Alternative: Create a separate file

File: resources/js/lang/index.ts

// Export all generated translations
export * from './en';
export * from './fr';
export * from './ar';
// ... add other locales as needed

File: resources/js/app.ts

import * as translations from './lang';

// ... in setup()
window.localizer = { translations };

3. Configure TypeScript (Optional)

Add types to your tsconfig.json:

{
  "compilerOptions": {
    "types": ["@devwizard/laravel-localizer-vue"]
  }
}

Usage

Basic Usage

{% raw %}
<script setup lang="ts">
import { useLocalizer } from '@devwizard/laravel-localizer-vue';

const { __ } = useLocalizer();
</script>

<template>
  <div>
    <h1>{{ __('welcome') }}</h1>
    <p>{{ __('validation.required') }}</p>
  </div>
</template>
{% endraw %}

With Replacements

{% raw %}
<script setup lang="ts">
import { useLocalizer } from '@devwizard/laravel-localizer-vue';

const { __ } = useLocalizer();
const userName = 'John';
const itemCount = 5;
</script>

<template>
  <div>
    <!-- Supports :placeholder format -->
    <p>{{ __('greeting', { name: userName }) }}</p>
    <!-- "Hello :name!" → "Hello John!" -->

    <!-- Also supports {placeholder} format -->
    <p>{{ __('items', { count: itemCount }) }}</p>
    <!-- "You have {count} items" → "You have 5 items" -->
  </div>
</template>
{% endraw %}

Pluralization

{% raw %}
<script setup lang="ts">
import { useLocalizer } from '@devwizard/laravel-localizer-vue';

const { choice } = useLocalizer();
const count = ref(5);
</script>

<template>
  <div>
    <!-- Define in your translation file: -->
    <!-- "apples": "no apples|one apple|many apples" -->

    <p>{{ choice('apples', count) }}</p>
    <!-- count = 0: "no apples" -->
    <!-- count = 1: "one apple" -->
    <!-- count = 5: "many apples" -->

    <!-- With replacements -->
    <p>{{ choice('apples', count, { count }) }}</p>
    <!-- "You have {count} apples" → "You have 5 apples" -->
  </div>
</template>
{% endraw %}

Checking Translation Existence

{% raw %}
<script setup lang="ts">
import { useLocalizer } from '@devwizard/laravel-localizer-vue';

const { __, has } = useLocalizer();
</script>

<template>
  <div>
    <h1 v-if="has('welcome')">{{ __('welcome') }}</h1>

    <p v-if="has('custom.message')">
      {{ __('custom.message') }}
    </p>
    <p v-else>Default message</p>
  </div>
</template>
{% endraw %}

With Fallback

{% raw %}
<script setup lang="ts">
import { useLocalizer } from '@devwizard/laravel-localizer-vue';

const { __ } = useLocalizer();
</script>

<template>
  <div>
    <!-- Use fallback for missing keys -->
    <p>{{ __('might.not.exist', {}, 'Default Text') }}</p>
  </div>
</template>
{% endraw %}

Locale Information (Reactive)

{% raw %}
<script setup lang="ts">
import { useLocalizer } from '@devwizard/laravel-localizer-vue';

const { locale, dir, availableLocales } = useLocalizer();
// Note: locale and dir are ComputedRef, so use .value in script
</script>

<template>
  <div :dir="dir">
    <p>Current Locale: {{ locale }}</p>
    <p>Text Direction: {{ dir }}</p>

    <select :value="locale">
      <option v-for="(meta, code) in availableLocales" :key="code" :value="code">
        {{ meta.flag }} {{ meta.label }}
      </option>
    </select>
  </div>
</template>
{% endraw %}

RTL Support

{% raw %}
<script setup lang="ts">
import { useLocalizer } from '@devwizard/laravel-localizer-vue';

const { __, dir } = useLocalizer();
</script>

<template>
  <div :dir="dir" :class="dir === 'rtl' ? 'text-right' : 'text-left'">
    <h1>{{ __('welcome') }}</h1>
    <p>{{ __('description') }}</p>
  </div>
</template>
{% endraw %}

Accessing All Translations

{% raw %}
<script setup lang="ts">
import { useLocalizer } from '@devwizard/laravel-localizer-vue';

const { translations } = useLocalizer();
</script>

<template>
  <div>
    <h2>All Translations:</h2>
    <pre>{{ JSON.stringify(translations, null, 2) }}</pre>
  </div>
</template>
{% endraw %}

API Reference

useLocalizer()

Returns an object with the following properties and methods:

| Property | Type | Description | | ------------------ | ------------------------------------------------------------------------- | ------------------------------- | | __ | (key: string, replacements?: Replacements, fallback?: string) => string | Main translation function | | trans | (key: string, replacements?: Replacements, fallback?: string) => string | Alias for __() | | lang | (key: string, replacements?: Replacements, fallback?: string) => string | Alias for __() | | has | (key: string) => boolean | Check if translation key exists | | choice | (key: string, count: number, replacements?: Replacements) => string | Pluralization support | | locale | ComputedRef<string> | Reactive current locale code | | dir | ComputedRef<'ltr' \| 'rtl'> | Reactive text direction | | availableLocales | ComputedRef<Record<string, LocaleMeta>> | Reactive available locales | | translations | ComputedRef<Record<string, string>> | Reactive all translations |

Note: Unlike React, locale, dir, availableLocales, and translations are ComputedRef values, making them fully reactive in Vue.

Vite Plugin Options

interface LocalizerOptions {
  // Watch patterns for language file changes
  patterns?: string[]; // default: ['lang/**', 'resources/lang/**']

  // Command to run when files change
  command?: string; // default: 'php artisan localizer:generate --all'

  // Enable debug logging
  debug?: boolean; // default: false
}

TypeScript Support

The package is written in TypeScript and provides full type definitions:

import {
  useLocalizer,
  UseLocalizerReturn,
  Replacements,
  LocaleData,
  PageProps,
} from '@devwizard/laravel-localizer-vue';

// All types are available for import

Testing

The package includes comprehensive tests using Vitest:

# Run tests
npm test

# Run tests in watch mode
npm run test:watch

# Generate coverage report
npm run test:coverage

Examples

Language Switcher

{% raw %}
<script setup lang="ts">
import { router } from '@inertiajs/vue3';
import { useLocalizer } from '@devwizard/laravel-localizer-vue';

const { locale, availableLocales } = useLocalizer();

const changeLocale = (newLocale: string) => {
  router.visit(route('locale.switch', { locale: newLocale }), {
    preserveScroll: true,
    preserveState: true,
  });
};
</script>

<template>
  <select :value="locale" @change="changeLocale($event.target.value)">
    <option v-for="(meta, code) in availableLocales" :key="code" :value="code">
      {{ meta.flag }} {{ meta.label }}
    </option>
  </select>
</template>
{% endraw %}

Form Validation

{% raw %}
<script setup lang="ts">
import { useLocalizer } from '@devwizard/laravel-localizer-vue';
import { useForm } from '@inertiajs/vue3';

const { __ } = useLocalizer();

const form = useForm({
  email: '',
  password: '',
});

const submit = () => {
  form.post('/login');
};
</script>

<template>
  <form @submit.prevent="submit">
    <div>
      <label>{{ __('auth.email') }}</label>
      <input v-model="form.email" type="email" required />
      <span v-if="form.errors.email" class="error">
        {{ form.errors.email }}
      </span>
    </div>

    <div>
      <label>{{ __('auth.password') }}</label>
      <input v-model="form.password" type="password" required />
      <span v-if="form.errors.password" class="error">
        {{ form.errors.password }}
      </span>
    </div>

    <button type="submit" :disabled="form.processing">
      {{ __('auth.login') }}
    </button>
  </form>
</template>
{% endraw %}

Composition API with Reactive Locale

{% raw %}
<script setup lang="ts">
import { computed } from 'vue';
import { useLocalizer } from '@devwizard/laravel-localizer-vue';

const { __, locale, dir } = useLocalizer();

// locale and dir are reactive, so this will update automatically
const greeting = computed(() => {
  return __('greeting', { locale: locale.value });
});

const containerClass = computed(() => ({
  'text-right': dir.value === 'rtl',
  'text-left': dir.value === 'ltr',
}));
</script>

<template>
  <div :class="containerClass">
    <h1>{{ greeting }}</h1>
    <p>Direction: {{ dir }}</p>
  </div>
</template>
{% endraw %}

Complete Working Example

Here's a full example of a multilingual user dashboard:

Backend: lang/en.json

{
  "welcome": "Welcome",
  "dashboard": "Dashboard",
  "greeting": "Hello, :name!",
  "notifications": "You have :count notifications"
}

Backend: lang/en/dashboard.php

<?php

return [
    'title' => 'User Dashboard',
    'stats' => [
        'users' => '{0} No users|{1} One user|[2,*] :count users',
        'posts' => 'You have :count posts',
    ],
];

Generate translations:

php artisan localizer:generate --all

Frontend: resources/js/Pages/Dashboard.vue

{% raw %}
<script setup lang="ts">
import { Head } from '@inertiajs/vue3';
import { useLocalizer } from '@devwizard/laravel-localizer-vue';
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout.vue';
import { PageProps } from '@/types';

interface DashboardProps extends PageProps {
  stats: {
    users: number;
    posts: number;
    notifications: number;
  };
}

const props = defineProps<DashboardProps>();
const { __, choice, locale, dir } = useLocalizer();
</script>

<template>
  <AuthenticatedLayout>
    <template #header>
      <h2 class="font-semibold text-xl text-gray-800 leading-tight">
        {{ __('dashboard.title') }}
      </h2>
    </template>

    <Head :title="__('dashboard')" />

    <div class="py-12" :dir="dir">
      <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
        <div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
          <div class="p-6 text-gray-900">
            <!-- Greeting with replacement -->
            <h1 class="text-2xl font-bold mb-4">
              {{ __('greeting', { name: auth.user.name }) }}
            </h1>

            <!-- Notification count -->
            <p class="mb-4">
              {{ __('notifications', { count: stats.notifications }) }}
            </p>

            <!-- Statistics with pluralization -->
            <div class="grid grid-cols-2 gap-4">
              <div class="p-4 bg-blue-50 rounded">
                <h3 class="font-semibold">Users</h3>
                <p>{{ choice('dashboard.stats.users', stats.users, { count: stats.users }) }}</p>
              </div>

              <div class="p-4 bg-green-50 rounded">
                <h3 class="font-semibold">Posts</h3>
                <p>{{ __('dashboard.stats.posts', { count: stats.posts }) }}</p>
              </div>
            </div>

            <!-- Locale info -->
            <div class="mt-4 text-sm text-gray-500">
              <p>Current locale: {{ locale }}</p>
              <p>Text direction: {{ dir }}</p>
            </div>
          </div>
        </div>
      </div>
    </div>
  </AuthenticatedLayout>
</template>
{% endraw %}

Language Switcher Component:

{% raw %}
<script setup lang="ts">
import { useLocalizer } from '@devwizard/laravel-localizer-vue';

const { locale, availableLocales } = useLocalizer();

const switchLocale = (newLocale: string) => {
  // Simple page reload with locale parameter
  window.location.href = `${window.location.pathname}?locale=${newLocale}`;
};
</script>

<template>
  <div class="relative">
    <select
      :value="locale"
      @change="switchLocale(($event.target as HTMLSelectElement).value)"
      class="block w-full px-3 py-2 border border-gray-300 rounded-md"
    >
      <option v-for="(meta, code) in availableLocales" :key="code" :value="code">
        {{ meta.flag }} {{ meta.label }}
      </option>
    </select>
  </div>
</template>
{% endraw %}

What happens:

  1. User changes locale in dropdown
  2. Page reloads with ?locale=fr parameter
  3. Laravel middleware detects locale and updates session
  4. Vue components re-render with new translations
  5. Text direction automatically adjusts for RTL languages

Development

# Install dependencies
npm install

# Run tests
npm test

# Build the package
npm run build

# Run linter
npm run lint

# Format code
npm run format

Contributing

Contributions are welcome! Please see CONTRIBUTING for details.

Changelog

Please see CHANGELOG for recent changes.

License

The MIT License (MIT). Please see License File for more information.

Related Packages

Credits