next-language-selector 0.3.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -7
- package/dist/index.cjs +1 -1
- package/dist/index.d.mts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.mjs +1 -1
- package/package.json +14 -4
package/README.md
CHANGED
|
@@ -9,12 +9,15 @@
|
|
|
9
9
|
A lightweight, unstyled language selector for Next.js (App Router & Pages Router).
|
|
10
10
|
Manages the `NEXT_LOCALE` cookie and works with `next-intl` or any i18n solution.
|
|
11
11
|
|
|
12
|
+
**[Live demo →](https://kirilinsky.github.io/next-language-selector/)**
|
|
13
|
+
|
|
12
14
|
- [Installation](#installation)
|
|
13
15
|
- [Basic Usage](#basic-usage)
|
|
14
16
|
- [Styling](#styling)
|
|
15
17
|
- [Custom UI](#custom-ui)
|
|
16
18
|
- [Dropdown mode](#dropdown-mode)
|
|
17
19
|
- [Setup with next-intl](#setup-with-next-intl)
|
|
20
|
+
- [Without full reloads](#without-full-reloads)
|
|
18
21
|
- [Props](#props)
|
|
19
22
|
- [`setLocaleCookie` utility](#setlocalecookie-utility)
|
|
20
23
|
- [SSR & hydration](#ssr--hydration)
|
|
@@ -152,15 +155,41 @@ export default createMiddleware({
|
|
|
152
155
|
});
|
|
153
156
|
```
|
|
154
157
|
|
|
158
|
+
## Without full reloads
|
|
159
|
+
|
|
160
|
+
By default the page reloads after a locale change so the server picks up the new cookie. For a smoother UX combine `autoReload={false}` with the `onChange` callback and the Next.js router:
|
|
161
|
+
|
|
162
|
+
```tsx
|
|
163
|
+
"use client";
|
|
164
|
+
|
|
165
|
+
import { useRouter } from "next/navigation";
|
|
166
|
+
import { LanguageSelector } from "next-language-selector";
|
|
167
|
+
|
|
168
|
+
export function LocaleSwitch() {
|
|
169
|
+
const router = useRouter();
|
|
170
|
+
return (
|
|
171
|
+
<LanguageSelector
|
|
172
|
+
locales={locales}
|
|
173
|
+
defaultLocale="en"
|
|
174
|
+
autoReload={false}
|
|
175
|
+
onChange={() => router.refresh()}
|
|
176
|
+
/>
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
`onChange` fires with the selected code before the cookie is written (and before the reload when `autoReload` is on) — also handy for analytics.
|
|
182
|
+
|
|
155
183
|
## Props
|
|
156
184
|
|
|
157
|
-
| Prop | Type
|
|
158
|
-
| :-------------- |
|
|
159
|
-
| `locales` | `LocaleConfig[]`
|
|
160
|
-
| `defaultLocale` | `string`
|
|
161
|
-
| `isDropdown` | `boolean`
|
|
162
|
-
| `autoReload` | `boolean`
|
|
163
|
-
| `
|
|
185
|
+
| Prop | Type | Default | Description |
|
|
186
|
+
| :-------------- | :----------------------- | :------------- | :--------------------------------------------------- |
|
|
187
|
+
| `locales` | `LocaleConfig[]` | **Required** | Array of `{ name, code, flag? }` objects |
|
|
188
|
+
| `defaultLocale` | `string` | **Required** | Fallback locale code |
|
|
189
|
+
| `isDropdown` | `boolean` | `false` | Render as `<select>` instead of buttons |
|
|
190
|
+
| `autoReload` | `boolean` | `true` | Reload page after cookie change |
|
|
191
|
+
| `onChange` | `(code: string) => void` | - | Called on selection, before cookie write/reload |
|
|
192
|
+
| `cookieName` | `string` | `NEXT_LOCALE` | Cookie name to store the selected locale |
|
|
164
193
|
| `className` | `string` | - | CSS class for the wrapper `<div>` or `<select>` |
|
|
165
194
|
| `itemClassName` | `string` | - | CSS class for each `<button>` or `<option>` |
|
|
166
195
|
| `renderCustom` | `Function` | - | Render prop for fully custom UI |
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var p=Object.defineProperty;var
|
|
1
|
+
"use strict";var p=Object.defineProperty;var R=Object.getOwnPropertyDescriptor;var h=Object.getOwnPropertyNames;var v=Object.prototype.hasOwnProperty;var x=(t,o)=>{for(var n in o)p(t,n,{get:o[n],enumerable:!0})},b=(t,o,n,a)=>{if(o&&typeof o=="object"||typeof o=="function")for(let r of h(o))!v.call(t,r)&&r!==n&&p(t,r,{get:()=>o[r],enumerable:!(a=R(o,r))||a.enumerable});return t};var P=t=>b(p({},"__esModule",{value:!0}),t);var w={};x(w,{LanguageSelector:()=>X,setLocaleCookie:()=>u});module.exports=P(w);var s=require("react");var S=(t="NEXT_LOCALE")=>{if(typeof document>"u")return null;let o=encodeURIComponent(t),n=document.cookie.split(";").map(a=>a.trim()).find(a=>a.startsWith(`${o}=`));if(!n)return null;try{return decodeURIComponent(n.slice(o.length+1))}catch{return null}},u=(t,o="NEXT_LOCALE",n=!0)=>{if(typeof document>"u")return;let a=encodeURIComponent(o),r=encodeURIComponent(t);document.cookie=`${a}=${r}; max-age=31536000; path=/; SameSite=Lax`,n&&window.location.reload()};var c=require("react/jsx-runtime");function k(t){let{locales:o,defaultLocale:n,cookieName:a="NEXT_LOCALE",isDropdown:r=!1,autoReload:d=!0,onChange:f,renderCustom:g,className:L,itemClassName:C}=t,[y,N]=(0,s.useState)(!1),[l,i]=(0,s.useState)(n);(0,s.useEffect)(()=>{let e=S(a);e&&o.some(E=>E.code===e)?i(e):i(n),N(!0)},[a,n,o]);let m=(0,s.useCallback)(e=>{i(e),f?.(e),u(e,a,d)},[a,d,f]);return y?g?(0,c.jsx)(c.Fragment,{children:g({locales:o,currentLocale:l,onChange:m})}):r?(0,c.jsx)("select",{value:l,onChange:e=>m(e.target.value),className:L,children:o.map(e=>(0,c.jsxs)("option",{value:e.code,className:C,children:[e.flag," ",e.name]},e.code))}):(0,c.jsx)("div",{className:L,children:o.map(e=>(0,c.jsxs)("button",{type:"button",onClick:()=>m(e.code),"data-active":l===e.code,"aria-pressed":l===e.code,className:C,children:[e.flag," ",e.name]},e.code))}):null}var X=k;0&&(module.exports={LanguageSelector,setLocaleCookie});
|
package/dist/index.d.mts
CHANGED
|
@@ -15,6 +15,12 @@ interface LanguageSelectorProps {
|
|
|
15
15
|
className?: string;
|
|
16
16
|
itemClassName?: string;
|
|
17
17
|
autoReload?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Called with the selected locale code after the internal state updates,
|
|
20
|
+
* before the cookie is written (and before the reload when autoReload is on).
|
|
21
|
+
* Use for analytics, router navigation or other side effects.
|
|
22
|
+
*/
|
|
23
|
+
onChange?: (code: string) => void;
|
|
18
24
|
renderCustom?: (props: {
|
|
19
25
|
locales: LocaleConfig[];
|
|
20
26
|
currentLocale: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -15,6 +15,12 @@ interface LanguageSelectorProps {
|
|
|
15
15
|
className?: string;
|
|
16
16
|
itemClassName?: string;
|
|
17
17
|
autoReload?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Called with the selected locale code after the internal state updates,
|
|
20
|
+
* before the cookie is written (and before the reload when autoReload is on).
|
|
21
|
+
* Use for analytics, router navigation or other side effects.
|
|
22
|
+
*/
|
|
23
|
+
onChange?: (code: string) => void;
|
|
18
24
|
renderCustom?: (props: {
|
|
19
25
|
locales: LocaleConfig[];
|
|
20
26
|
currentLocale: string;
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{useCallback as
|
|
1
|
+
import{useCallback as R,useEffect as h,useState as C}from"react";var L=(a="NEXT_LOCALE")=>{if(typeof document>"u")return null;let o=encodeURIComponent(a),n=document.cookie.split(";").map(t=>t.trim()).find(t=>t.startsWith(`${o}=`));if(!n)return null;try{return decodeURIComponent(n.slice(o.length+1))}catch{return null}},u=(a,o="NEXT_LOCALE",n=!0)=>{if(typeof document>"u")return;let t=encodeURIComponent(o),c=encodeURIComponent(a);document.cookie=`${t}=${c}; max-age=31536000; path=/; SameSite=Lax`,n&&window.location.reload()};import{Fragment as v,jsx as i,jsxs as S}from"react/jsx-runtime";function k(a){let{locales:o,defaultLocale:n,cookieName:t="NEXT_LOCALE",isDropdown:c=!1,autoReload:m=!0,onChange:p,renderCustom:d,className:f,itemClassName:g}=a,[y,N]=C(!1),[r,s]=C(n);h(()=>{let e=L(t);e&&o.some(E=>E.code===e)?s(e):s(n),N(!0)},[t,n,o]);let l=R(e=>{s(e),p?.(e),u(e,t,m)},[t,m,p]);return y?d?i(v,{children:d({locales:o,currentLocale:r,onChange:l})}):c?i("select",{value:r,onChange:e=>l(e.target.value),className:f,children:o.map(e=>S("option",{value:e.code,className:g,children:[e.flag," ",e.name]},e.code))}):i("div",{className:f,children:o.map(e=>S("button",{type:"button",onClick:()=>l(e.code),"data-active":r===e.code,"aria-pressed":r===e.code,className:g,children:[e.flag," ",e.name]},e.code))}):null}var O=k;export{O as LanguageSelector,u as setLocaleCookie};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "next-language-selector",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Configurable language selector for Next.js",
|
|
5
5
|
"main": "./dist/index.cjs",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
"lint": "tsc --noEmit",
|
|
19
19
|
"test": "vitest",
|
|
20
20
|
"test:run": "vitest run",
|
|
21
|
-
"test:coverage": "npx vitest run --coverage"
|
|
21
|
+
"test:coverage": "npx vitest run --coverage",
|
|
22
|
+
"demo:build": "esbuild demo/main.tsx --bundle --minify --jsx=automatic --outfile=dist-demo/main.js && cp demo/index.html demo/og.png dist-demo/"
|
|
22
23
|
},
|
|
23
24
|
"files": [
|
|
24
25
|
"dist"
|
|
@@ -39,7 +40,7 @@
|
|
|
39
40
|
],
|
|
40
41
|
"author": "Kirilinsky (https://github.com/kirilinsky)",
|
|
41
42
|
"license": "MIT",
|
|
42
|
-
"homepage": "https://github.
|
|
43
|
+
"homepage": "https://kirilinsky.github.io/next-language-selector/",
|
|
43
44
|
"bugs": {
|
|
44
45
|
"url": "https://github.com/kirilinsky/next-language-selector/issues"
|
|
45
46
|
},
|
|
@@ -52,13 +53,22 @@
|
|
|
52
53
|
"@testing-library/react": "^16.3.2",
|
|
53
54
|
"@testing-library/user-event": "^14.6.1",
|
|
54
55
|
"@types/react": "^19.2.14",
|
|
56
|
+
"@types/react-dom": "^19.2.3",
|
|
55
57
|
"@vitest/coverage-v8": "^4.1.2",
|
|
58
|
+
"esbuild": "^0.28.1",
|
|
56
59
|
"happy-dom": "^20.8.9",
|
|
60
|
+
"react": "^19.2.7",
|
|
61
|
+
"react-dom": "^19.2.7",
|
|
57
62
|
"tslib": "^2.8.1",
|
|
58
63
|
"tsup": "^8.5.1",
|
|
59
64
|
"typescript": "^5.9.3",
|
|
60
65
|
"vitest": "^4.1.2"
|
|
61
66
|
},
|
|
62
67
|
"sideEffects": false,
|
|
63
|
-
"packageManager": "pnpm@10.32.1"
|
|
68
|
+
"packageManager": "pnpm@10.32.1",
|
|
69
|
+
"pnpm": {
|
|
70
|
+
"onlyBuiltDependencies": [
|
|
71
|
+
"esbuild"
|
|
72
|
+
]
|
|
73
|
+
}
|
|
64
74
|
}
|