PHPackages                             ozner-omali/laravel-to-i18next - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. ozner-omali/laravel-to-i18next

ActiveLibrary

ozner-omali/laravel-to-i18next
==============================

Parse Laravel language files into i18next-compatible JSON

v0.1.0(9mo ago)16MITPHPPHP ^8.4.0CI passing

Since Aug 3Pushed 9mo agoCompare

[ Source](https://github.com/LorenzoWynberg/laravel-to-i18next)[ Packagist](https://packagist.org/packages/ozner-omali/laravel-to-i18next)[ RSS](/packages/ozner-omali-laravel-to-i18next/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (1)Dependencies (12)Versions (3)Used By (0)

Laravel → i18next Lang Parser
=============================

[](#laravel--i18next-lang-parser)

This package parses your [Laravel translation](https://laravel.com/docs/12.x/localization) files into [i18next](https://www.i18next.com/)-compatible JSON, converting plural forms and placeholders into a format ready for dynamic translation usage in the frontend.

---

🎬 Features
----------

[](#-features)

```
- Reads every `lang/{locale}/*.php` file
- Parses Laravel’s `trans_choice` plural syntax (`{0}|{1}|[2,*]`)
- Converts `:placeholder`, `:Placeholder`, `:PLACEHOLDER` into i18next interpolations:
    - `:foo` → `{{foo}}`
    - `:Foo` → `{{foo, capitalize}}`
    - `:FOO` → `{{foo, uppercase}}`
- Strips attributes from HTML tags
- Writes to `public/locales/{locale}/*.json`
- Generates a `public/locales/versions.json` file with:
    - A unique hash for each locale’s translation files
    - A `last_updated` timestamp
    - Useful for cache invalidation and detecting translation updates
- Artisan command: `lang:to-i18next {locale?}`

```

---

📦 Installation
--------------

[](#-installation)

```
composer require ozner-omali/laravel-to-i18next

```

---

🔧 Usage
-------

[](#-usage)

```
1. Export all locales
    php artisan lang:to-i18next
2. Export a specific locale
    php artisan lang:to-i18next es

```

This will generate:
• Translation files under: /public/locales/{locale}/\*.json
• A version tracking file under: /public/locales/versions.json

### Example versions.json file

[](#example-versionsjson-file)

```
{
    "en": {
        "hash": "a1b2c3d4e5f6g7h8i9j0",
        "last_updated": "2025-06-01T12:00:00Z"
    },
    "es": {
        "hash": "0j9i8h7g6f5e4d3c2b1a",
        "last_updated": "2025-06-01T12:30:00Z"
    }
}
```

### Example Laravel Translations:

[](#example-laravel-translations)

// resources/lang/en/messages.php

```
return [
    'success' => [
        'created' => '{0} No :resource created.|{1} :Resource created successfully.|[2,*] Many :resource created successfully.'
    ],
];
```

// resources/lang/en/models.php

```
return [
    'user' => 'user|users',
    'address' => 'address|addresses',
];
```

### Generated i18next JSON:

[](#generated-i18next-json)

// resources/locales/en/messages.json

```
{
    "success": {
        "created_zero": "No {{resource}} created.",
        "created_one": "{{resource, capitalize}} created successfully.",
        "created_other": "Many {{resource}} created successfully."
    }
}
```

// resources/lang/en/models.json

```
{
    "user_one": "user",
    "user_other": "users",
    "address_one": "address",
    "address_other": "addresses"
}
```

---

⚙️ Front-end Integration
------------------------

[](#️-front-end-integration)

### 1. Installation

[](#1-installation)

```
npm install i18next react-i18next i18next-http-backend react-native-localize
```

### 2. useLangStore.ts (Zustand store)

[](#2-uselangstorets-zustand-store)

Define a store with:

- hydrated: boolean — whether storage has finished hydration
- lang: string — current language
- versions: Record&lt;string, { hash: string; last\_updated: string }&gt; — the translation version hashes
- getVersion(lang) to fetch hash or fallback to "latest"

```
import { create } from 'zustand';
import { immer } from 'zustand/middleware/immer';
import { persist, createJSONStorage } from 'zustand/middleware';
import AsyncStorage from '@react-native-async-storage/async-storage';

type VersionsMap = Record;

type LangStore = {
  hydrated: boolean;

  lang: string;
  setLang: (lang: string) => void;

  versions: VersionsMap;
  getVersion: (lang: string) => string;
  setVersions: (versions: VersionsMap) => void;
};

export const useLangStore = create()(
  persist(
    immer((set, get) => ({
      // Hydration
      hydrated: false,

      // Language
      lang: 'en',
      setLang: lang =>
        set(state => {
          state.lang = lang;
        }),

      // Versions
      versions: {},
      getVersion: lang => {
        return get().versions[lang]?.hash ?? 'latest';
      },
      setVersions: versions =>
        set(state => {
          state.versions = { ...versions };
        }),
    })),
    {
      name: 'lang-storage',
      storage: createJSONStorage(() => AsyncStorage),
      onRehydrateStorage: () => {
        return (_state, error) => {
          if (!error) {
            useLangStore.setState({ hydrated: true });
          }
        };
      },
      partialize: (state): Pick => ({
        lang: state.lang,
        versions: state.versions,
      }),
    },
  ),
);
```

### 3. useFetchVersions.ts (Hook to sync version.json)

[](#3-usefetchversionsts-hook-to-sync-versionjson)

```
import { useEffect } from 'react';
import { BACKEND_URL } from '@env';
import { useLangStore } from '@/stores/useLangStore';
import i18next from '@/config/i18next';

export const useFetchVersions = (hydrated: Boolean) => {
  const setVersions = useLangStore.getState().setVersions;
  const getVersions = useLangStore.getState().versions;

  useEffect(() => {
    if (!hydrated) return;
    const fetchVersions = async () => {
      try {
        // Append timestamp to bust all caches
        const timestamp = Date.now();
        const url = `${BACKEND_URL}/locales/versions.json?ts=${timestamp}`;

        const res = await fetch(url);

        if (!res.ok)
          throw new Error(`Failed to fetch versions (${res.status})`);

        const data = await res.json();

        const previous = getVersions;
        const changed = JSON.stringify(previous) !== JSON.stringify(data);

        if (changed) {
          console.log(
            '[i18n - useFetchVersions] VERSIONS UPDATED, reloading translations...',
          );
          setVersions(data);

          const currentLng = i18next.language || 'en';
          const namespaces = i18next.options.ns as string[];

          await i18next.reloadResources([currentLng], namespaces);
        } else {
          console.log(
            '[i18n - useFetchVersions] VERSIONS UNCHANGED, using cached translations.',
          );
        }
      } catch (e) {
        console.warn(
          '[i18n - useFetchVersions] Failed to fetch versions.json:',
          e,
        );
      }
    };

    fetchVersions();
  }, [hydrated]);
};
```

### 4. config/i18next.ts (Initialization)

[](#4-configi18nextts-initialization)

```
import i18next from 'i18next';
import { BACKEND_URL } from '@env';
import { initReactI18next } from 'react-i18next';
import { useLangStore } from '@/stores/useLangStore';
import HttpApi, { HttpBackendOptions } from 'i18next-http-backend';

const fallbackLng = 'en';
const namespaces = [
    'addresses',
    'auth',
    'cache',
    'common',
    'http',
    'models',
    'pagination',
    'passwords',
    'resource',
    'validation',
];

export const initI18n = async () => {
    const store = useLangStore.getState();
    const initialLang = store.lang;

    return i18next
        .use(HttpApi)
        .use(initReactI18next)
        .init({
            lng: initialLang,
            fallbackLng,
            ns: namespaces,
            defaultNS: 'common',
            backend: {
                loadPath: (lngs: string[], namespaces: string[]): string => {
                    const lng = lngs[0];
                    const ns = namespaces[0];
                    const version = useLangStore.getState().getVersion(lng);
                    console.log(
                        `[i18n] Loading translations for ${lng}/${ns} (version: ${version})`,
                    );
                    return `${BACKEND_URL}/locales/${lng}/${ns}.json?v=${version}`;
                },
            },
            interpolation: {
                escapeValue: false,
                format: (value, format) => {
                    if (typeof value !== 'string') return value;
                    if (format === 'capitalize') {
                        return value.charAt(0).toUpperCase() + value.slice(1);
                    }
                    if (format === 'uppercase') {
                        return value.toUpperCase();
                    }
                    return value;
                },
            },
            react: {
                useSuspense: false,
            },
        });
};

export default i18next;
```

### 5. App.tsx (Entry point)

[](#5-apptsx-entry-point)

```
import i18next from './config/i18next';
import { initI18n } from './config/i18next';
import { useEffect, useState } from 'react';
import { I18nextProvider } from 'react-i18next';
import { useLangStore } from './stores/useLangStore';
import { ThemeProvider } from './theme/ThemeProvider';
import { View, ActivityIndicator } from 'react-native';
import AppNavigation from './navigations/AppNavigation';
import { useFetchVersions } from './hooks/useFetchVersions';
import { SafeAreaProvider } from 'react-native-safe-area-context';

export default function App() {
  const langStoreHydrated = useLangStore(state => state.hydrated);
  const [i18nReady, setI18nReady] = useState(false);

  // Fetch versions only after hydration
  useFetchVersions(langStoreHydrated);

  useEffect(() => {
    if (langStoreHydrated) {
      initI18n().then(() => setI18nReady(true));
    }
  }, [langStoreHydrated]);

  if (!langStoreHydrated || !i18nReady) {
    return (

    );
  }

  return (

  );
}
```

### 6. Basic Example Usage

[](#6-basic-example-usage)

```
// passing 2 when count is 0 or less on the model
// so that the pluralization in this case is correct
// Note: could have also set up the pluralization for the resource
// on the laravel end with {0}, {1}, [2,*]
// then you could use the same count in the model translation

let count = null;
let resource = null;

// 0 items
count = 0;
resource = i18next.t('models.user', {count: (count  '{0} Hello :name, there are none
            |{1} Hello :name, there is one apple
            |[2,*] Hello :name, there are :count apples',
```

#### B. Generated i18next JSON: public/locales/en/{file}.json

[](#b-generated-i18next-json-publiclocalesenfilejson)

```
{
  "apples_zero": "Hello {{name}}, there are none",
  "apples_one": "Hello {{name}}, there is one apple",
  "apples_other": "Hello {{name}}, there are {{count}} apples"
}
```

#### C. /components/i18Next/TransComponents.tsx

[](#c-componentsi18nexttranscomponentstsx)

```
import React from 'react';
import { Text } from 'react-native';

export const transComponents = {
    // Bold text
    b: ,
    bold: ,

    // Italic text
    i: ,
    italic: ,

    // Line breaks
    br: {'\n'},

    // Span (generic inline container)
    span: ,
};
```

#### D. Usage in component

[](#d-usage-in-component)

```

```

#### E. Output

[](#e-output)

```
Hello Ozner, there are none
```

---

✅ Notes
-------

[](#-notes)

```
The parser:
  • Converts Laravel’s pipe plural syntax ('user|users') into _one and _other keys.
  • Supports Laravel’s pluralization brackets ({0}, {1}, [2,*]) and converts them to *_zero, *_one, *_other.
  • Placeholders are automatically transformed:
     • :key → {{key}}
     • :Key → {{key, capitalize}}
     • :KEY → {{key, uppercase}}
  • Strips HTML attributes from tags, e.g.,  → .
  • Adds versions.json for change detection and frontend cache management.

```

---

📖 Changelog
-----------

[](#-changelog)

```
See CHANGELOG.md for release notes and breaking changes.
```

🤝 Contributing
--------------

[](#-contributing)

```
Contributions, issues, and feature requests are welcome!

🔗 Report Issues
🔗 Submit Pull Requests
```

🔑 License
---------

[](#-license)

```
🔑 MIT License © Lorenzo Wynberg / Ozner Omali
```

🎵 Like my code? You'll love my music!
-------------------------------------

[](#-like-my-code-youll-love-my-music)

- [Apple Music](https://music.apple.com/us/album/the-kitty-cat-crew/1796753922)
- [Spotify](https://open.spotify.com/album/0uTRS5Z5Qebgi7BavwGlpm)

###  Health Score

31

—

LowBetter than 68% of packages

Maintenance57

Moderate activity, may be stable

Popularity7

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity45

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

2

Last Release

282d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/64c38c770c82086eaddb3f713ddcf27147be29a1a8fed3f65723e04d9033200f?d=identicon)[LorenzoWynberg](/maintainers/LorenzoWynberg)

---

Top Contributors

[![LorenzoWynberg](https://avatars.githubusercontent.com/u/7880769?v=4)](https://github.com/LorenzoWynberg "LorenzoWynberg (1 commits)")

---

Tags

phppackagei18next

###  Code Quality

TestsPest

Static AnalysisPHPStan, Rector

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/ozner-omali-laravel-to-i18next/health.svg)

```
[![Health](https://phpackages.com/badges/ozner-omali-laravel-to-i18next/health.svg)](https://phpackages.com/packages/ozner-omali-laravel-to-i18next)
```

###  Alternatives

[larastan/larastan

Larastan - Discover bugs in your code without running it. A phpstan/phpstan extension for Laravel

6.4k43.5M5.2k](/packages/larastan-larastan)[laravel/wayfinder

Generate TypeScript representations of your Laravel actions and routes.

1.7k4.1M69](/packages/laravel-wayfinder)[tehwave/laravel-achievements

Simple, elegant Achievements the Laravel way

7012.8k](/packages/tehwave-laravel-achievements)[aedart/athenaeum

Athenaeum is a mono repository; a collection of various PHP packages

255.2k](/packages/aedart-athenaeum)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
