Vite plugin to client bundle i18next locales composited from one to many json/yaml files from one to many libraries. Zero config HMR support included.

Overview

vite-plugin-i18next-loader

npm version

yarn add -D vite-plugin-i18next-loader

Vite plugin to client bundle i18next locales composited from one to many json/yaml files from one to many libraries. Zero config HMR support included.

This vite-plugin i18next loader generates the resources structure necessary for i18next. The structure is made available as a virtual module to the client bundle at build time, thus avoiding loading any language resources via extra HTTP requests.

Features

  • glob based file filtering
  • one to many overrides supporting reuse cases (white labeling)
  • yaml and json support
  • hot module reloading (Basic with full reload works, HMR may be improved with vite 3.2 api - see #5)
  • chunking/tree shaking may already be possible, see #4 - needs more trial/discussion.

Given a locales directory, by default, the loader will find and parse any json|yaml|yml file and attribute the contents to the containing lang folder e.g. en. There is no need to add lang such as en or de inside your json or yaml files.

See the test/data directory for structure and example data.

Usage

Sample app structure

└── app
    └── src
    │  └── index.js
    └── locales
       ├── de
       │   ├── foo.json
       │   └── bar.yaml
       └── en
           ├── foo.json
           └── bar.yaml

vite.config.ts

import { defineConfig } from 'vite'
import i18nextLoader from 'vite-plugin-i18next-loader'

export default defineConfig({
  plugins: [i18nextLoader({ paths: ['./node_modules/foo/locales', './locales'] })],
})

app.ts

// File: app.ts
import i18n from 'i18next'
import resources from 'virtual:i18next-loader'

i18n.init({
  resources,
})

// Use the resources as documented on i18next.com
i18n.t('key')

Options

export interface Options {
  /**
   * Set to 'info' for noisy information.
   *
   * Default: 'warn'
   */
  logLevel?: LogLevel

  /**
   * Glob patterns to match files
   *
   * Default: ['**\/*.json', '**\/*.yml', '**\/*.yaml']
   */
  include?: string[]

  /**
   * Locale top level directory paths ordered from least specialized to most specialized
   *  e.g. lib locale -> app locale
   *
   * Locales loaded later will overwrite any duplicated key via a deep merge strategy.
   */
  paths: string[]

  /**
   * Default: none
   */
  namespaceResolution?: 'basename' | 'relativePath'
}

include to filtering files read

You can filter files in your file structure by specifying any glob supported by glob-all. By default, any json|yaml|yml in the paths directories will be loaded.

Only json

{
  include: ['**/*.json']
}

All json except one file

{
  include: ['**/*.json', '!**/excludeThis.json']
}

paths for overriding/white labeling

Applications that reuse libraries e.g. white labeling, can utilize one to many sets of locale directories that the app will override.

{
  paths: ['../node_modules/lib1/locales', './locales'] // from least to most specialized
}

This configures the loader to work on a file structure like the following:

└── app
    ├── src
    │  └── app.js
    ├── locales
    │  └── en
    │      ├── foo.json
    │      └── bar.yaml
    └── node_modules
        └── lib1
            └── locales
               └── en
                   ├── foo.json
                   └── bar.yaml

Everything from ./locales will override anything specified in one to many libraries.

namespaceResolution

Namespace resolution will impact the structure of the bundle. If you want the files' basename or relative path to be injected, look at the following options.

namespaceResolution: 'basename'

{
  namespaceResolution: 'basename'
}

The following file structure would result in resources loaded as below:

└── app
    ├── src
    │  └── index.js
    └── locales
       └── en
           ├── foo.json
           └── bar.yaml

foo.json

{
  "header": {
    "title": "TITLE"
  }
}

bar.yaml

footer:
  aboutUs: About us

Results in this object loaded:

{
  "en": {
    "foo": {
      "header": {
        "title": "TITLE"
      }
    },
    "bar": {
      "footer": {
        "aboutUs": "About us"
      }
    }
  }
}

namespaceResolution: 'relativePath'

{
  namespaceResolution: 'relativePath'
}

The following file structure would result in resources loaded as below:

└── app
    └── locales
       ├── index.js
       └── en
           ├── green.yaml
           ├── blue
           ├──── foo.yaml

green.yaml

tree:
  species: Oak

blue/foo.yaml

water:
  ocean: Quite large

Results in this object loaded:

{
  "en": {
    "green": {
      "tree": {
        "species": "Oak"
      }
    },
    "blue": {
      "foo": {
        "water": {
          "ocean": "Quite large"
        }
      }
    }
  }
}

NOTE: If you have a file and a folder with the same name, you MIGHT overwrite one with the other. For example:

└── app
    └── locales
       ├── index.js
       └── en
           ├── blue.yaml
           ├── blue
           ├──── foo.yaml

blue.yaml

foo: Welcome

blue/foo.yaml

eggs: delicious

Results in this object loaded:

{
  "en": {
    "blue": {
      "foo": {
        "eggs": "delicious"
      }
    }
  }
}

But it's just overwriting based on the return value of glob-all, so you shouldn't depend on it.

Output

Note that the virtual module generated has contents that conform to the i18next resource format.

While using the output with import resources from 'virtual:i18next-loader' will not be tree-shaken, it is possible to use the named outputs with a dynamic import for tree shaking/chunking optimizations. If you take advantage of this, please see #4 and take a moment to update this doc with more information.

NOTE as shown by the test output below, due to ES syntactical rules, we cannot use hyphenated lang codes. I'm open to ideas, but in the interim, affected lang codes are exported with the hyphen converted to underscore e.g. zh-cn has a named export of zh_cn. I noted that vite allows for tree-shaking of JSON files, perhaps that is worth looking at to consider how it might help us and inform our output?

export const en = {
  foo: { test: 'app foo.test en' },
  main: {
    test: 'app test en',
    sub: {
      slug: 'app sub.slug en',
      test: 'lib sub.test en',
      subsub: { slugslug: 'app sub.subsub.slugsub en', test: 'lib sub.subsub.test en' },
    },
  },
}
export const zh_cn = {
  foo: { test: 'app foo.test zh-cn' },
  main: {
    test: 'app test zh-cn',
    sub: {
      slug: 'app sub.slug zh-cn',
      test: 'lib sub.test zh-cn',
      subsub: { slugslug: 'app sub.subsub.slugsub zh-cn', test: 'lib sub.subsub.test zh-cn' },
    },
  },
}
const resources = {
  en,
  'zh-cn': zh_cn,
}
export default resources

Credit

This was forked from @alienfast/i18next-loader, converted to be a vite plugin and improved. Thanks to the original authors and contributors.

Comments
  • Update build config

    Update build config

    • allow lock file changes to be committed that work
    • separate build, test, release scripts
    • use different/updated actions
    • fix repo in package.json
    📦 Published PR as canary version: 2.0.2--canary.7.5f0440e.0

    :sparkles: Test out this PR locally via:

    npm install [email protected]
    # or 
    yarn add [email protected]
    
    opened by rosskevin 0
  • Add basic hot module reloading (hmr)

    Add basic hot module reloading (hmr)

    Closes #2

    This implements:

    • basic hot module reloading - causes full reload (improvements noted can be made with the release of vite 3.2)
    • improved logging using the vite logger
    • change to options debug is now logLevel (config breaking change)
    • improved logging to the terminal of the final bundle using marked
    📦 Published PR as canary version: 1.1.0--canary.3.6f7836d.0

    :sparkles: Test out this PR locally via:

    npm install [email protected]
    # or 
    yarn add [email protected]
    
    major 
    opened by rosskevin 0
  • 1.0.0 Release

    1.0.0 Release

    opened by rosskevin 0
  • Error [ERR_REQUIRE_ESM]: lodash-es

    Error [ERR_REQUIRE_ESM]: lodash-es

    Hi,

    i'm trying to use this plugin in my project and I've followed the instructions of the plugin. I'm using vite version 4.0.1 and I've installed latest version of vite-plugin-i18next-loader which is 2.0.2.

    This is my vite.config.ts file:

    
    import react from '@vitejs/plugin-react';
    import { resolve } from 'path';
    import { defineConfig } from 'vite';
    import i18nextLoader from 'vite-plugin-i18next-loader';
    import mkcert from 'vite-plugin-mkcert';
    import svgrPlugin from 'vite-plugin-svgr';
    import viteTsconfigPaths from 'vite-tsconfig-paths';
    
    import * as baseTheme from './src/styles/baseTheme.json';
    
    export default defineConfig({
      server: {
        https: true,
        port: 3000,
        open: true,
      },
      build: {
        outDir: 'build',
      },
      css: {
        preprocessorOptions: {
          less: {
            modifyVars: {
              // Color Palettes
              '@blue-1': baseTheme.colorPalettes.daybreakBlue['blue-1'],
              ...
            },
            javascriptEnabled: true,
          },
        },
      },
      plugins: [
        react(),
        viteTsconfigPaths(),
        mkcert(),
        svgrPlugin(),
        i18nextLoader({
          paths: ['./src/locales/en'],
        }),
      ],
    });
    

    When I try to start my project with vite I get this error:

    failed to load config from ./vite.config.ts error when starting dev server: Error [ERR_REQUIRE_ESM]: require() of ES Module /node_modules/lodash-es/lodash.js from /node_modules/vite-plugin-i18next-loader/dist/index.cjs not supported. Instead change the require of lodash.js in /node_modules/vite-plugin-i18next-loader/dist/index.cjs to a dynamic import() which is available in all CommonJS modules. at Object._require.extensions. [as .js] (file:///node_modules/vite/dist/node/chunks/dep-5605cfa4.js:63157:17) at Object. (/node_modules/vite-plugin-i18next-loader/dist/index.cjs:1:666) at Object._require.extensions. [as .js] (file://node_modules/vite/dist/node/chunks/dep-5605cfa4.js:63157:17) at Object. (/vite.config.ts:35:49) at Object._require.extensions. [as .js] (file://node_modules/vite/dist/node/chunks/dep-5605cfa4.js:63154:24) at loadConfigFromBundledFile (file://node_modules/vite/dist/node/chunks/dep-5605cfa4.js:63162:21) at loadConfigFromFile (file://node_modules/vite/dist/node/chunks/dep-5605cfa4.js:63020:34) at async resolveConfig (file://node_modules/vite/dist/node/chunks/dep-5605cfa4.js:62643:28) at async createServer (file://node_modules/vite/dist/node/chunks/dep-5605cfa4.js:61943:20) at async CAC. (file://node_modules/vite/dist/node/cli.js:707:24)

    Thank you

    opened by alveshelio 0
  • Cannot find module 'virtual:i18next-loader' or its corresponding type declarations

    Cannot find module 'virtual:i18next-loader' or its corresponding type declarations

    I'm pretty new to vite and i18next, so this is probably my mistake, but I'm getting the following error when I try to setup the loader, any advice?

    image
    yarn run v1.22.19
    src/i18n.tsx:6:23 - error TS2307: Cannot find module 'virtual:i18next-loader' or its corresponding type declarations.
    
    6 import resources from 'virtual:i18next-loader';
                            ~~~~~~~~~~~~~~~~~~~~~~~~```
    opened by marcopelegrini 3
  • Improve HMR with vite 3.2 api change

    Improve HMR with vite 3.2 api change

    This is already commented in the code, but hopefully the new api will negate the need to send a full page reload.

    Waiting on this PR to be released in vite 3.2 https://github.com/vitejs/vite/pull/10333/files

    server. reloadModule(module)

    enhancement 
    opened by rosskevin 0
  • Chunking/tree shaking via lang-based named imports

    Chunking/tree shaking via lang-based named imports

    Since the output of the plugin is an ESM export, perhaps it makes sense for us to enumerate langs and export them independently at the top, in addition to one default export (which includes all langs for those that don't care).

    I suspect that a named dynamic import would then be tree-shakeable. I could be wrong, but interested in other's thoughts.

    documentation enhancement 
    opened by rosskevin 2
Releases(v2.0.2)
Owner
AlienFast
AlienFast
Add aliasing support to Vite from tsconfig.json or jsconfig.json files

Config to Alias Config to Alias adds aliasing support to Astro, JavaScript, TypeScript, and CSS files. Usage Install Config to Alias. npm install @ast

Astro Community 4 Mar 17, 2023
Chrome Extension Boilerplate with SolidJS + Vite + TypeScript + Manifest V3 + HMR

mv3-solid-chrome-extension-template chrome-extension development template with firebase Support Chrome Extension Manifest V3 SolidJS Typescript HMR Fi

munron 6 Dec 13, 2022
Generate a zodios (typescript http client with zod validation) from an OpenAPI spec (json/yaml)

openapi-zod-client Generates a zodios (typescript http client with zod validation) from a (json/yaml) OpenAPI spec (or just use the generated schemas/

Alexandre Stahmer 104 Jan 4, 2023
GetOsLocalesCrossPlatform - A cross platform alternative to get locales used on the platform. Works on Node, Electron, NW.js and Browsers

getOsLocalesCrossPlatform A cross platform alternative to get locales used on the platform. Works on Node, Electron, NW.js and Browsers This script is

null 1 Jan 2, 2022
An Obsidian plugin to grab all yaml fields from all files into a dataframe

Metadataframe Metadataframe allows you to get all metadata from your vault into CSV file. With CSV in-hand, you can do any data analysis you want with

null 7 Sep 15, 2022
Opinionated collection of TypeScript definitions and utilities for Deno and Deno Deploy. With complete types for Deno/NPM/TS config files, constructed from official JSON schemas.

Schemas Note: You can also import any type from the default module, ./mod.ts deno.json import { type DenoJson } from "https://deno.land/x/[email protected]

deno911 2 Oct 12, 2022
Download all Moodle files with one click. This is a Chrome extension built to save time and effort from downloading files manually one by one!

Moodle Downloader Extension Moodle downloader extension for Chrome. The extension is tested with both the TUM moodle and the official moodle demo. Not

Zhongpin Wang 8 Nov 15, 2022
Zero-config PWA Plugin for VitePress

Zero-config PWA Plugin for VitePress ?? Features ?? Documentation & guides ?? Zero-Config: sensible built-in default configs for common use cases ?? E

Vite PWA 10 Dec 1, 2022
Out-of-the-box MPA plugin for Vite, with html template engine and virtual files support.

vite-plugin-virtual-mpa Out-of-the-box MPA plugin for Vite, with html template engine and virtual files support, generate multiple files using only on

QinXuYang 21 Dec 16, 2022
⚡ It is a simplified database module with multiple functions that you can use simultaneously with sqlite, yaml, firebase and json.

Prisma Database Developed with ?? by Roxza ⚡ An easy, open source database ?? Installation npm i prisma.db --save yarn add prisma.db ?? Importing impo

Roxza 21 Jan 3, 2023
Visualize and download JSON / YAML content

Graphize ?? Visualize and download JSON / YAML content in your browser Demo Key Features Preview your JSON / YAML documents instantly Pinch / Scroll Z

Varun A P 25 Dec 26, 2022
A custom action for setting GitHub Workflow environment variables with YAML configuration files.

yaml-env-action - A custom action for setting GitHub Workflow environment variables with YAML configuration files. Introduction yaml-env-action is a c

Piper Dougherty 3 Dec 13, 2022
Deduplication tool for pnpm-lock.yaml files

pnpm-deduplicate Remove duplicate dependencies from pnpm-lock.yaml. This project is simple and not have many features. I see it as a temporary solutio

null 62 Jan 3, 2023
Multi-platform node package bundle to a package.json.

dmpc Multi-platform node package bundle to a package.json. install ### npm mode npm i -g @kingsword/dmpc ### yarn mode yarn global add @kingsword/dmp

Kingsword 2 Oct 16, 2022
Multi-level contextmenu created in Vanilla Javascript (no css files included)

ContexMenu.js Multi-level ContextMenu Created in Vanilla Javascript (No CSS Files) Import with jsDelivr <script src="https://cdn.jsdelivr.net/gh/L1qui

null 5 Nov 22, 2022
Internationalization for svelte framework. Based on i18next ecosystem

svelte-i18next Svelte wrapper for i18next npm i svelte-i18next i18next Implementation This library wraps an i18next instance in a Svelte Store to obs

Nishu Goel 20 Dec 9, 2022
A new zero-config test runner for the true minimalists

Why User-friendly - zero-config, no API to learn, simple conventions Extremely lighweight - only 40 lines of code and no dependencies Super fast - wit

null 680 Dec 20, 2022
📦 🍣 Zero-config JS bundler for ESM, CommonJS, and .d.ts outputs

pkgroll Write your code in ESM & TypeScript and bundle it to get ESM, CommonJS, and type declaration outputs with a single command! Features Zero conf

hiroki osame 153 Dec 23, 2022
📦 🍣 Zero-config JS bundler for ESM, CommonJS, and .d.ts outputs. (Forked from pkgroll)

?? ?? puild (A fork of pkgroll) Write your code in ESM & TypeScript and bundle it to get ESM, CommonJS, and type declaration outputs with a single com

ʀᴀʏ 6 Sep 6, 2022