@korioinc/next-core 2.0.34 → 2.0.35

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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/locale-switcher/index.tsx"],"names":[],"mappings":"AAcA,UAAU,mBAAmB;IAC3B;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,CAAC,OAAO,UAAU,cAAc,CAAC,EAAE,SAAS,EAAE,GAAE,mBAAwB,kDAgF7E"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/locale-switcher/index.tsx"],"names":[],"mappings":"AAeA,UAAU,mBAAmB;IAC3B;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,CAAC,OAAO,UAAU,cAAc,CAAC,EAAE,SAAS,EAAE,GAAE,mBAAwB,kDAiG7E"}
@@ -7,6 +7,7 @@ import { Menu, MenuButton, MenuItem, MenuItems, Transition } from '@headlessui/r
7
7
  import { useLingui } from '@lingui/react';
8
8
  import { allLanguages } from './language-options';
9
9
  import { resolveVisibleLanguages } from './resolve-visible-languages';
10
+ import { setCookie } from 'cookies-next/client';
10
11
  import linguiConfig from 'lingui.config';
11
12
  export default function LocaleSwitcher({ languages } = {}) {
12
13
  const router = useRouter();
@@ -16,6 +17,15 @@ export default function LocaleSwitcher({ languages } = {}) {
16
17
  useEffect(() => {
17
18
  setMounted(true);
18
19
  }, []);
20
+ useEffect(() => {
21
+ if (!mounted || !i18n.locale) {
22
+ return;
23
+ }
24
+ setCookie('NEXT_LOCALE', i18n.locale, {
25
+ path: '/',
26
+ sameSite: 'lax',
27
+ });
28
+ }, [mounted, i18n.locale]);
19
29
  const availableLanguages = resolveVisibleLanguages({
20
30
  allLanguages,
21
31
  defaultLanguageCodes: linguiConfig.locales,
@@ -30,6 +40,10 @@ export default function LocaleSwitcher({ languages } = {}) {
30
40
  const changeLanguage = (nextLocale) => {
31
41
  const currentLocale = i18n.locale;
32
42
  const newPath = getChangeLocalePath(pathname, currentLocale, nextLocale);
43
+ setCookie('NEXT_LOCALE', nextLocale, {
44
+ path: '/',
45
+ sameSite: 'lax',
46
+ });
33
47
  router.replace(newPath);
34
48
  router.refresh();
35
49
  };
@@ -0,0 +1,2 @@
1
+ export declare function useToLocalePath(): (href?: string | null) => string;
2
+ //# sourceMappingURL=routing.client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routing.client.d.ts","sourceRoot":"","sources":["../../src/i18n/routing.client.ts"],"names":[],"mappings":"AAMA,wBAAgB,eAAe,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,KAAK,MAAM,CAIlE"}
@@ -0,0 +1,7 @@
1
+ 'use client';
2
+ import { usePathname } from 'next/navigation';
3
+ import { toLocalePath as buildLocalePath } from './routing';
4
+ export function useToLocalePath() {
5
+ const pathname = usePathname();
6
+ return (href) => buildLocalePath(pathname, href);
7
+ }
@@ -8,6 +8,7 @@ export declare function getLocales(): string[];
8
8
  * Get default locale
9
9
  */
10
10
  export declare function getDefaultLocale(): string;
11
+ export declare function getPathLocale(pathname: string): string | undefined;
11
12
  export declare function handleLocaleRouting(request: NextRequest): NextResponse<unknown>;
12
13
  export declare function getRequestLocale(requestHeaders: Headers): string;
13
14
  export declare function getCookieLocale(requestHeaders: Headers, pathname?: string): string | undefined;
@@ -18,6 +19,10 @@ export declare function getPathname({ locale, href }: {
18
19
  }): string;
19
20
  export declare function getLanguageAlternates(pathname?: string): Record<string, string>;
20
21
  export declare function getCurrentPathname(): Promise<string>;
22
+ export declare function getCurrentPathLocale(): Promise<string | undefined>;
23
+ export declare function getPathLocalePathname(pathname: string, href?: string | null): string;
24
+ export declare function toLocalePath(pathname: string, href?: string | null): string;
25
+ export declare function getLocalePathname(href: string): Promise<string>;
21
26
  export declare function getCurrentSearchParams(): Promise<URLSearchParams>;
22
27
  export declare function getDefaultPathname(): Promise<string>;
23
28
  export declare function getCurrentLanguageAlternates(): Promise<Record<string, string>>;
@@ -1 +1 @@
1
- {"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../src/i18n/routing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,KAAK,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAsC7D;;GAEG;AACH,wBAAgB,UAAU,IAAI,MAAM,EAAE,CAErC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAiGD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,WAAW,yBAsEvD;AAED,wBAAgB,gBAAgB,CAAC,cAAc,EAAE,OAAO,GAAG,MAAM,CAShE;AAED,wBAAgB,eAAe,CAAC,cAAc,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAW9F;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAoBvG;AAED,wBAAgB,WAAW,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,UAY7E;AAsBD,wBAAgB,qBAAqB,CAAC,QAAQ,GAAE,MAAY,0BAwB3D;AAED,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC,CAK1D;AAED,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,eAAe,CAAC,CAMvE;AAED,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC,CAU1D;AAED,wBAAsB,4BAA4B,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAIpF;AAED,KAAK,aAAa,GACd,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,GAC7C,eAAe,GACf,uBAAuB,GACvB,SAAS,CAAC;AAiDd,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,MAAM,CAErF;AAED,wBAAsB,UAAU,CAAC,MAAM,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAIxE;AAED,wBAAsB,2BAA2B,IAAI,OAAO,CAAC,MAAM,CAAC,CAKnE"}
1
+ {"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../src/i18n/routing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,KAAK,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAoC7D;;GAEG;AACH,wBAAgB,UAAU,IAAI,MAAM,EAAE,CAErC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AA4BD,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAElE;AAiFD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,WAAW,yBA2EvD;AAED,wBAAgB,gBAAgB,CAAC,cAAc,EAAE,OAAO,GAAG,MAAM,CAShE;AAED,wBAAgB,eAAe,CAAC,cAAc,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAW9F;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAevG;AAED,wBAAgB,WAAW,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,UAY7E;AAsBD,wBAAgB,qBAAqB,CAAC,QAAQ,GAAE,MAAY,0BAwB3D;AAED,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC,CAK1D;AAED,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAKxE;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAuBpF;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAE3E;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAKrE;AAED,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,eAAe,CAAC,CAMvE;AAED,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC,CAU1D;AAED,wBAAsB,4BAA4B,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAIpF;AAED,KAAK,aAAa,GACd,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,GAC7C,eAAe,GACf,uBAAuB,GACvB,SAAS,CAAC;AAiDd,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,MAAM,CAErF;AAED,wBAAsB,UAAU,CAAC,MAAM,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAIxE;AAED,wBAAsB,2BAA2B,IAAI,OAAO,CAAC,MAAM,CAAC,CAKnE"}
@@ -19,11 +19,10 @@ const DEFAULT_FALLBACK = () => {
19
19
  const LOCALE_COOKIE_NAME = 'NEXT_LOCALE';
20
20
  const LOCALE_PREFIX = 'as-needed';
21
21
  const PATHNAME_HEADER_NAME = 'x-pathname';
22
+ const PATH_LOCALE_HEADER_NAME = 'x-path-locale';
22
23
  const SEARCH_PARAMS_HEADER_NAME = 'x-search-params';
23
- const setRoutingHeaders = (response, pathname, search) => {
24
- response.headers.set(PATHNAME_HEADER_NAME, pathname);
25
- response.headers.set(SEARCH_PARAMS_HEADER_NAME, search.replace(/^\?/, ''));
26
- return response;
24
+ const isDocumentRequest = (request) => {
25
+ return request.headers.get('accept')?.includes('text/html') || false;
27
26
  };
28
27
  /**
29
28
  * Get all available locales
@@ -57,6 +56,16 @@ const getPathLocaleSegment = (pathname) => {
57
56
  const pathMatch = pathname.match(/^\/([^\\/]+)(?:\/|$)/);
58
57
  return pathMatch?.[1];
59
58
  };
59
+ export function getPathLocale(pathname) {
60
+ return getCanonicalLocale(getPathLocaleSegment(pathname));
61
+ }
62
+ const setRoutingHeaders = (response, pathname, search) => {
63
+ const pathLocale = getCanonicalLocale(getPathLocaleSegment(pathname));
64
+ response.headers.set(PATHNAME_HEADER_NAME, pathname);
65
+ response.headers.set(PATH_LOCALE_HEADER_NAME, pathLocale || '');
66
+ response.headers.set(SEARCH_PARAMS_HEADER_NAME, search.replace(/^\?/, ''));
67
+ return response;
68
+ };
60
69
  const replacePathLocale = (pathname, locale) => {
61
70
  return pathname.replace(/^\/[^\\/]+(?=\/|$)/, `/${locale}`);
62
71
  };
@@ -107,6 +116,7 @@ export function handleLocaleRouting(request) {
107
116
  const localeCookie = request.cookies.get(LOCALE_COOKIE_NAME);
108
117
  const cookieLocale = getCanonicalLocale(localeCookie?.value);
109
118
  const defaultFallback = DEFAULT_FALLBACK();
119
+ const documentRequest = isDocumentRequest(request);
110
120
  // 1. URL에서 locale 감지
111
121
  const pathLocaleSegment = getPathLocaleSegment(pathname);
112
122
  const pathLocale = getCanonicalLocale(pathLocaleSegment);
@@ -114,7 +124,7 @@ export function handleLocaleRouting(request) {
114
124
  // 2. URL에 locale이 포함된 경우
115
125
  if (pathnameHasLocale) {
116
126
  if (pathLocaleSegment !== pathLocale) {
117
- return redirectWithLocale(request, pathname, pathLocale, cookieLocale !== pathLocale);
127
+ return redirectWithLocale(request, pathname, pathLocale, cookieLocale !== pathLocale && documentRequest);
118
128
  }
119
129
  // 이미 path에 locale이 있을 때
120
130
  const response = setRoutingHeaders(NextResponse.next(), pathname, request.nextUrl.search);
@@ -122,9 +132,12 @@ export function handleLocaleRouting(request) {
122
132
  if (cookieLocale !== pathLocale) {
123
133
  // 기본 locale인 경우 URL에서 제거
124
134
  if (LOCALE_PREFIX === 'as-needed' && pathLocale === defaultFallback) {
125
- return redirectWithoutLocale(request, pathname, pathLocale, true);
135
+ return redirectWithoutLocale(request, pathname, pathLocale, documentRequest);
126
136
  }
127
137
  // 다른 locale의 경우 쿠키 설정
138
+ if (!documentRequest) {
139
+ return response;
140
+ }
128
141
  return setCookie(response, pathLocale);
129
142
  }
130
143
  // locale은 같지만 기본 locale이고 as-needed 설정인 경우
@@ -154,7 +167,7 @@ export function handleLocaleRouting(request) {
154
167
  request.nextUrl.pathname = newPathname;
155
168
  const response = setRoutingHeaders(NextResponse.redirect(request.nextUrl), newPathname, request.nextUrl.search);
156
169
  // 쿠키가 없는 경우 설정
157
- if (localeCookie?.value === undefined) {
170
+ if (localeCookie?.value === undefined && documentRequest) {
158
171
  setCookie(response, locale);
159
172
  }
160
173
  return response;
@@ -178,25 +191,20 @@ export function getCookieLocale(requestHeaders, pathname) {
178
191
  return getCanonicalLocale(getPathLocaleSegment(pathname));
179
192
  }
180
193
  export function getChangeLocalePath(pathname, currentLocale, nextLocale) {
181
- // Create the new path based on whether the current path has a locale segment or not
182
194
  let pathNameWithoutLocale;
183
- // Check if pathname starts with current locale
184
195
  if (pathname === `/${currentLocale}` || pathname.startsWith(`/${currentLocale}/`)) {
185
- // Path has locale: /ko or /ko/something
186
- pathNameWithoutLocale = pathname.slice(currentLocale.length + 1); // Remove /locale part
196
+ pathNameWithoutLocale = pathname.slice(currentLocale.length + 1);
187
197
  if (pathNameWithoutLocale.startsWith('/')) {
188
- pathNameWithoutLocale = pathNameWithoutLocale.slice(1); // Remove leading slash if any
198
+ pathNameWithoutLocale = pathNameWithoutLocale.slice(1);
189
199
  }
190
200
  }
191
201
  else if (pathname === '/') {
192
- // Root path
193
202
  pathNameWithoutLocale = '';
194
203
  }
195
204
  else {
196
- // Path doesn't have locale prefix: /something
197
205
  pathNameWithoutLocale = pathname.startsWith('/') ? pathname.slice(1) : pathname;
198
206
  }
199
- return `/${nextLocale}${pathNameWithoutLocale ? '/' + pathNameWithoutLocale : ''}`;
207
+ return `/${nextLocale}${pathNameWithoutLocale ? `/${pathNameWithoutLocale}` : ''}`;
200
208
  }
201
209
  export function getPathname({ locale, href }) {
202
210
  const defaultFallback = DEFAULT_FALLBACK();
@@ -251,6 +259,37 @@ export async function getCurrentPathname() {
251
259
  const headersList = await headers();
252
260
  return headersList.get(PATHNAME_HEADER_NAME) || '';
253
261
  }
262
+ export async function getCurrentPathLocale() {
263
+ const { headers } = await import('next/headers');
264
+ const headersList = await headers();
265
+ return getCanonicalLocale(headersList.get(PATH_LOCALE_HEADER_NAME) || undefined);
266
+ }
267
+ export function getPathLocalePathname(pathname, href) {
268
+ if (href == null || href === '#') {
269
+ return '#';
270
+ }
271
+ const normalizedHref = href === '' ? '/' : href.startsWith('/') ? href : `/${href}`;
272
+ const hrefLocale = getPathLocale(normalizedHref);
273
+ if (hrefLocale) {
274
+ return normalizedHref;
275
+ }
276
+ const pathLocale = getPathLocale(pathname);
277
+ if (!pathLocale) {
278
+ return normalizedHref;
279
+ }
280
+ if (normalizedHref === '/') {
281
+ return `/${pathLocale}`;
282
+ }
283
+ return `/${pathLocale}${normalizedHref}`;
284
+ }
285
+ export function toLocalePath(pathname, href) {
286
+ return getPathLocalePathname(pathname, href);
287
+ }
288
+ export async function getLocalePathname(href) {
289
+ const pathLocale = await getCurrentPathLocale();
290
+ const currentPathname = pathLocale ? `/${pathLocale}` : '/';
291
+ return getPathLocalePathname(currentPathname, href);
292
+ }
254
293
  export async function getCurrentSearchParams() {
255
294
  const { headers } = await import('next/headers');
256
295
  const headersList = await headers();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@korioinc/next-core",
3
- "version": "2.0.34",
3
+ "version": "2.0.35",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./ads": {
@@ -90,7 +90,7 @@
90
90
  "tw-animate-css": "^1.4.0",
91
91
  "typescript": "^6.0.2",
92
92
  "vitest": "^4.1.2",
93
- "@korioinc/next-configs": "2.0.34"
93
+ "@korioinc/next-configs": "2.0.35"
94
94
  },
95
95
  "dependencies": {
96
96
  "@floating-ui/react": "^0.27.19",
@@ -105,7 +105,7 @@
105
105
  "schema-dts": "^2.0.0",
106
106
  "tailwind-merge": "^3.5.0",
107
107
  "valtio": "^2.3.1",
108
- "@korioinc/next-conf": "2.0.34"
108
+ "@korioinc/next-conf": "2.0.35"
109
109
  },
110
110
  "publishConfig": {
111
111
  "access": "public",