cloudflare-next-intl 0.1.2 → 0.2.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.
Files changed (34) hide show
  1. package/README.md +129 -4
  2. package/dist/src/client/components/locale_link.d.ts +22 -0
  3. package/dist/src/client/components/locale_link.js +22 -0
  4. package/dist/src/client/functions/get_cookie.d.ts +9 -0
  5. package/dist/src/client/functions/get_cookie.js +9 -0
  6. package/dist/src/client/functions/set_cookie.d.ts +14 -0
  7. package/dist/src/client/functions/set_cookie.js +14 -0
  8. package/dist/src/client/hooks/client_hooks.d.ts +20 -0
  9. package/dist/src/client/hooks/client_hooks.js +20 -0
  10. package/dist/src/client/hooks/use_path_name.d.ts +9 -0
  11. package/dist/src/client/hooks/use_path_name.js +9 -0
  12. package/dist/src/config/init_config.d.ts +16 -0
  13. package/dist/src/config/init_config.js +16 -0
  14. package/dist/src/config/middleware.d.ts +13 -0
  15. package/dist/src/config/middleware.js +13 -1
  16. package/dist/src/general/get_layout_states.js +4 -0
  17. package/dist/src/general/metadata.d.ts +25 -0
  18. package/dist/src/general/metadata.js +25 -0
  19. package/dist/src/server/components/helper_script.d.ts +19 -0
  20. package/dist/src/server/components/helper_script.js +19 -0
  21. package/dist/src/server/components/link.d.ts +20 -0
  22. package/dist/src/server/components/link.js +20 -0
  23. package/dist/src/server/components/server_provider.d.ts +29 -0
  24. package/dist/src/server/components/server_provider.js +29 -0
  25. package/dist/src/server/functions/locale_static_params.d.ts +12 -0
  26. package/dist/src/server/functions/locale_static_params.js +12 -0
  27. package/dist/src/server/functions/server.d.ts +41 -0
  28. package/dist/src/server/functions/server.js +41 -0
  29. package/dist/src/server/functions/use_functions.d.ts +22 -0
  30. package/dist/src/server/functions/use_functions.js +22 -0
  31. package/dist/src/theme_switcher/components/theme_switcher.d.ts +10 -0
  32. package/dist/src/theme_switcher/components/theme_switcher.js +10 -0
  33. package/dist/src/types/types.d.ts +55 -0
  34. package/package.json +1 -1
package/README.md CHANGED
@@ -18,22 +18,72 @@ and Cloudflare environment.
18
18
  npm install cloudflare-next-intl
19
19
  ```
20
20
 
21
- ## Usage
21
+ ## Setup
22
22
 
23
- ### Configuration
23
+ This package resolves your routing config through the `@intl-config` module
24
+ alias, so setup has two required steps — both must be done for the package
25
+ to work.
24
26
 
25
- Set up your internationalization configuration:
27
+ ### 1. Create your config file
26
28
 
27
29
  ```typescript
30
+ // src/i18n/intl_config.ts
28
31
  import { setIntlConfig } from "cloudflare-next-intl/setIntlConfig";
29
32
 
30
33
  export default setIntlConfig({
31
34
  locales: ["en", "de"],
32
35
  defaultLocale: "en",
33
- // ... other config
36
+ // ... other RoutingConfig fields (localePrefix, localeCookie, localeDetection)
34
37
  });
35
38
  ```
36
39
 
40
+ ### 2. Point `@intl-config` at it in `next.config`
41
+
42
+ Required for both webpack and Turbopack — omitting either breaks the build
43
+ mode that uses it. Path is relative to `next.config`.
44
+
45
+ ```typescript
46
+ // next.config.ts
47
+ import type { NextConfig } from "next";
48
+ import path from "path";
49
+
50
+ const nextConfig: NextConfig = {
51
+ turbopack: {
52
+ resolveAlias: {
53
+ "@intl-config": "./src/i18n/intl_config.ts",
54
+ },
55
+ },
56
+ webpack(config) {
57
+ config.resolve.alias = {
58
+ ...config.resolve.alias,
59
+ "@intl-config": path.resolve(__dirname, "src/i18n/intl_config"),
60
+ };
61
+ return config;
62
+ },
63
+ };
64
+
65
+ export default nextConfig;
66
+ ```
67
+
68
+ If this alias is missing, `cloudflare-next-intl/middleware` throws
69
+ `Please set config file and set path to it in next.config as in the example`
70
+ at startup.
71
+
72
+ ### 3. Wire up the middleware
73
+
74
+ ```typescript
75
+ // src/middleware.ts
76
+ import intlMiddleware from "cloudflare-next-intl/middleware";
77
+
78
+ export const middleware = intlMiddleware;
79
+
80
+ export const config = {
81
+ matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
82
+ };
83
+ ```
84
+
85
+ ## Usage
86
+
37
87
  ### Server Components
38
88
 
39
89
  ```tsx
@@ -61,6 +111,81 @@ export function Navigation() {
61
111
  }
62
112
  ```
63
113
 
114
+ ### Locale-aware links (non-locale-switching)
115
+
116
+ Use `Link` (server-safe, from `./Link`) for normal navigation that should
117
+ stay on the current locale — it prepends the locale segment for you:
118
+
119
+ ```tsx
120
+ import Link from "cloudflare-next-intl/Link";
121
+
122
+ <Link href="/about">About</Link> // -> "/about" or "/de/about"
123
+ ```
124
+
125
+ `LocaleLink` (client-only) is for explicitly switching locale, e.g. a
126
+ language switcher — see the Client Components example above.
127
+
128
+ ### Reading the current locale
129
+
130
+ ```tsx
131
+ // Server Components
132
+ import { getLocale } from "cloudflare-next-intl/server";
133
+ const locale = await getLocale();
134
+ ```
135
+
136
+ ```tsx
137
+ // Client Components ("use client")
138
+ import { useLocale } from "cloudflare-next-intl/use";
139
+ const locale = useLocale();
140
+ ```
141
+
142
+ `useTranslations`/`useLocale` from `cloudflare-next-intl/use` also work
143
+ inside Server Components without `await` (backed by React's `use()`), as
144
+ long as they're rendered under `IntlProvider`.
145
+
146
+ ### Root layout wiring
147
+
148
+ ```tsx
149
+ import { IntlProvider, IntlHelperScript, getLocaleStaticParams } from "cloudflare-next-intl/server";
150
+
151
+ export const generateStaticParams = getLocaleStaticParams;
152
+
153
+ export default async function RootLayout({ children, params }) {
154
+ const { locale } = await params;
155
+ return (
156
+ <html lang={locale}>
157
+ <head>
158
+ <IntlHelperScript />
159
+ </head>
160
+ <body>
161
+ <IntlProvider language={locale}>{children}</IntlProvider>
162
+ </body>
163
+ </html>
164
+ );
165
+ }
166
+ ```
167
+
168
+ ### SEO metadata (hreflang/canonical)
169
+
170
+ ```ts
171
+ import { alternatesLinks } from "cloudflare-next-intl/metadata";
172
+
173
+ export async function generateMetadata({ params }) {
174
+ const { locale } = await params;
175
+ return {
176
+ alternates: alternatesLinks({ url: "https://example.com", locale, linkPart: "/about" }),
177
+ };
178
+ }
179
+ ```
180
+
181
+ ### Theme switcher
182
+
183
+ ```tsx
184
+ import ThemeSwitcher from "cloudflare-next-intl/ThemeSwitcher";
185
+
186
+ <ThemeSwitcher lightLabelText="Light" darkLabelText="Dark" />
187
+ ```
188
+
64
189
  ## License
65
190
 
66
191
  MIT
@@ -4,5 +4,27 @@ type NextLinkProps = Omit<ComponentProps<'a'>, keyof LinkProps> & Omit<LinkProps
4
4
  export type LocaleLinkProps = NextLinkProps & {
5
5
  locale: string;
6
6
  };
7
+ /**
8
+ * Client-only link component for linking to a SPECIFIC locale — e.g. a
9
+ * language switcher. Import from `cloudflare-next-intl/LocaleLink`.
10
+ *
11
+ * For normal in-app navigation that should stay on the current locale, use
12
+ * the server-side `Link` (`cloudflare-next-intl/Link`) instead — it infers
13
+ * the locale automatically and doesn't require `"use client"`.
14
+ *
15
+ * Renders inside a `Suspense` boundary; falls back to a disabled `<a>`
16
+ * (`pointer-events-none`) while resolving.
17
+ *
18
+ * @param locale Required. The locale to link to (e.g. `"de"`), prepended to
19
+ * `href` regardless of the current locale.
20
+ *
21
+ * @example
22
+ * ```tsx
23
+ * "use client";
24
+ * import LocaleLink from "cloudflare-next-intl/LocaleLink";
25
+ *
26
+ * <LocaleLink href="/about" locale="de">Über uns</LocaleLink> // -> "/de/about"
27
+ * ```
28
+ */
7
29
  declare const LocaleLink: import("react").ForwardRefExoticComponent<Omit<LocaleLinkProps, "ref"> & import("react").RefAttributes<HTMLAnchorElement>>;
8
30
  export default LocaleLink;
@@ -4,5 +4,27 @@ import LocaleLinkClient from './locale_link_client';
4
4
  function LocaleLinkComponent(params, ref) {
5
5
  return _jsx(Suspense, { fallback: _jsx("a", { ...params, ref: ref, className: params.className + ' pointer-events-none' }), children: _jsx(LocaleLinkClient, { ref: ref, ...params }) });
6
6
  }
7
+ /**
8
+ * Client-only link component for linking to a SPECIFIC locale — e.g. a
9
+ * language switcher. Import from `cloudflare-next-intl/LocaleLink`.
10
+ *
11
+ * For normal in-app navigation that should stay on the current locale, use
12
+ * the server-side `Link` (`cloudflare-next-intl/Link`) instead — it infers
13
+ * the locale automatically and doesn't require `"use client"`.
14
+ *
15
+ * Renders inside a `Suspense` boundary; falls back to a disabled `<a>`
16
+ * (`pointer-events-none`) while resolving.
17
+ *
18
+ * @param locale Required. The locale to link to (e.g. `"de"`), prepended to
19
+ * `href` regardless of the current locale.
20
+ *
21
+ * @example
22
+ * ```tsx
23
+ * "use client";
24
+ * import LocaleLink from "cloudflare-next-intl/LocaleLink";
25
+ *
26
+ * <LocaleLink href="/about" locale="de">Über uns</LocaleLink> // -> "/de/about"
27
+ * ```
28
+ */
7
29
  const LocaleLink = forwardRef(LocaleLinkComponent);
8
30
  export default LocaleLink;
@@ -1 +1,10 @@
1
+ /**
2
+ * Client-only: reads a `document.cookie` value by name. Pairs with
3
+ * {@link setCookie} for your OWN client-side cookies — this package's
4
+ * locale/theme cookies are managed internally, you don't need this for those.
5
+ *
6
+ * @param name Cookie name to look up.
7
+ * @returns The decoded cookie value, or `null` if not found (or on error,
8
+ * logged instead of thrown).
9
+ */
1
10
  export default function getCookie(name: string): string | null;
@@ -1,4 +1,13 @@
1
1
  "use client";
2
+ /**
3
+ * Client-only: reads a `document.cookie` value by name. Pairs with
4
+ * {@link setCookie} for your OWN client-side cookies — this package's
5
+ * locale/theme cookies are managed internally, you don't need this for those.
6
+ *
7
+ * @param name Cookie name to look up.
8
+ * @returns The decoded cookie value, or `null` if not found (or on error,
9
+ * logged instead of thrown).
10
+ */
2
11
  export default function getCookie(name) {
3
12
  try {
4
13
  const match = document.cookie.match(new RegExp(`(?:^|; )${name}=([^;]*)`));
@@ -1,3 +1,17 @@
1
+ /**
2
+ * Client-only: sets a `document.cookie` value directly. Not used by this
3
+ * package's own locale handling (that goes through `intlMiddleware` server-side)
4
+ * — this is a small utility for your OWN client-side cookies (e.g. a
5
+ * "dismissed banner" flag).
6
+ *
7
+ * @param name Cookie name.
8
+ * @param value Cookie value; stringified via template literal (no encoding
9
+ * applied — avoid values containing `;`).
10
+ * @param maxAge Seconds until expiry. Defaults to 1 year.
11
+ *
12
+ * Always `path=/; SameSite=Lax`. Swallows errors (e.g. in restrictive
13
+ * environments) and logs them instead of throwing.
14
+ */
1
15
  export default function setCookie({ name, value, maxAge }: {
2
16
  name: string;
3
17
  value: unknown;
@@ -1,4 +1,18 @@
1
1
  "use client";
2
+ /**
3
+ * Client-only: sets a `document.cookie` value directly. Not used by this
4
+ * package's own locale handling (that goes through `intlMiddleware` server-side)
5
+ * — this is a small utility for your OWN client-side cookies (e.g. a
6
+ * "dismissed banner" flag).
7
+ *
8
+ * @param name Cookie name.
9
+ * @param value Cookie value; stringified via template literal (no encoding
10
+ * applied — avoid values containing `;`).
11
+ * @param maxAge Seconds until expiry. Defaults to 1 year.
12
+ *
13
+ * Always `path=/; SameSite=Lax`. Swallows errors (e.g. in restrictive
14
+ * environments) and logs them instead of throwing.
15
+ */
2
16
  export default function setCookie({ name, value, maxAge }) {
3
17
  try {
4
18
  const cookieString = `${name}=${value}; path=/; max-age=${maxAge ?? 31536000}; SameSite=Lax;`;
@@ -1,3 +1,23 @@
1
1
  import type { TranslatorReturnType } from "../../types/types";
2
+ /**
3
+ * Client Component `useLocale` — reached via the default condition of the
4
+ * `cloudflare-next-intl/use` subpath (Server Components get the
5
+ * `use_functions.ts` variant automatically instead; always import from
6
+ * `"cloudflare-next-intl/use"`, never this file directly).
7
+ *
8
+ * Reads the locale from React context set up by `IntlProvider`.
9
+ *
10
+ * @returns The current locale (e.g. `"en"`).
11
+ * @throws If rendered outside `IntlProvider`.
12
+ */
2
13
  export declare function useLocale(): string;
14
+ /**
15
+ * Client Component `useTranslations` — see {@link useLocale} for the
16
+ * subpath-resolution note.
17
+ *
18
+ * @param namespace Dot-separated key prefix into your messages file.
19
+ * @returns A `(key: string) => string` translation function, memoized on
20
+ * `[language, messages, namespace]`.
21
+ * @throws If rendered outside `IntlProvider`.
22
+ */
3
23
  export declare function useTranslations(namespace: string): TranslatorReturnType;
@@ -2,6 +2,17 @@
2
2
  import { useContext, useMemo } from "react";
3
3
  import { LocaleContext } from "../components/client_provider";
4
4
  import { getTranslationsImpl } from "../../general/general_functions";
5
+ /**
6
+ * Client Component `useLocale` — reached via the default condition of the
7
+ * `cloudflare-next-intl/use` subpath (Server Components get the
8
+ * `use_functions.ts` variant automatically instead; always import from
9
+ * `"cloudflare-next-intl/use"`, never this file directly).
10
+ *
11
+ * Reads the locale from React context set up by `IntlProvider`.
12
+ *
13
+ * @returns The current locale (e.g. `"en"`).
14
+ * @throws If rendered outside `IntlProvider`.
15
+ */
5
16
  export function useLocale() {
6
17
  const context = useContext(LocaleContext);
7
18
  if (context === undefined) {
@@ -9,6 +20,15 @@ export function useLocale() {
9
20
  }
10
21
  return context.language;
11
22
  }
23
+ /**
24
+ * Client Component `useTranslations` — see {@link useLocale} for the
25
+ * subpath-resolution note.
26
+ *
27
+ * @param namespace Dot-separated key prefix into your messages file.
28
+ * @returns A `(key: string) => string` translation function, memoized on
29
+ * `[language, messages, namespace]`.
30
+ * @throws If rendered outside `IntlProvider`.
31
+ */
12
32
  export function useTranslations(namespace) {
13
33
  const context = useContext(LocaleContext);
14
34
  if (context === undefined) {
@@ -1 +1,10 @@
1
+ /**
2
+ * Client hook: like `next/navigation`'s `usePathname`, but with the locale
3
+ * segment stripped — so `/de/about` returns `/about`, matching the
4
+ * locale-agnostic paths used elsewhere in this package (e.g. `Link` href).
5
+ * Must be used inside `IntlProvider`/`LocaleContext` (via {@link useLocale}).
6
+ *
7
+ * @returns The current pathname without its locale prefix (e.g. `/about`, or
8
+ * `/` for the root).
9
+ */
1
10
  export default function usePathname(): string;
@@ -1,6 +1,15 @@
1
1
  "use client";
2
2
  import { usePathname as nextUsePathname } from "next/navigation";
3
3
  import { useLocale } from "./client_hooks";
4
+ /**
5
+ * Client hook: like `next/navigation`'s `usePathname`, but with the locale
6
+ * segment stripped — so `/de/about` returns `/about`, matching the
7
+ * locale-agnostic paths used elsewhere in this package (e.g. `Link` href).
8
+ * Must be used inside `IntlProvider`/`LocaleContext` (via {@link useLocale}).
9
+ *
10
+ * @returns The current pathname without its locale prefix (e.g. `/about`, or
11
+ * `/` for the root).
12
+ */
4
13
  export default function usePathname() {
5
14
  const pathname = nextUsePathname();
6
15
  const locale = useLocale();
@@ -1,2 +1,18 @@
1
1
  import type { LocalePrefixMode, Locales, RoutingConfig } from '../types/types';
2
+ /**
3
+ * Defines and type-checks your app's i18n routing config.
4
+ *
5
+ * Identity function at runtime — it exists purely so TypeScript infers
6
+ * `AppLocales`/`AppLocalePrefixMode` from the literal config object you pass
7
+ * in, giving you autocomplete/type errors on `locale` params elsewhere.
8
+ *
9
+ * Export the result from the file referenced by `@intl-config` (see your
10
+ * `next.config`), e.g.:
11
+ * ```ts
12
+ * export default setIntlConfig({
13
+ * locales: ["en", "fr"] as const,
14
+ * defaultLocale: "en",
15
+ * });
16
+ * ```
17
+ */
2
18
  export declare function setIntlConfig<const AppLocales extends Locales, const AppLocalePrefixMode extends LocalePrefixMode = 'as-needed'>(config: RoutingConfig<AppLocales, AppLocalePrefixMode>): RoutingConfig<AppLocales, AppLocalePrefixMode>;
@@ -1,3 +1,19 @@
1
+ /**
2
+ * Defines and type-checks your app's i18n routing config.
3
+ *
4
+ * Identity function at runtime — it exists purely so TypeScript infers
5
+ * `AppLocales`/`AppLocalePrefixMode` from the literal config object you pass
6
+ * in, giving you autocomplete/type errors on `locale` params elsewhere.
7
+ *
8
+ * Export the result from the file referenced by `@intl-config` (see your
9
+ * `next.config`), e.g.:
10
+ * ```ts
11
+ * export default setIntlConfig({
12
+ * locales: ["en", "fr"] as const,
13
+ * defaultLocale: "en",
14
+ * });
15
+ * ```
16
+ */
1
17
  export function setIntlConfig(config) {
2
18
  return config;
3
19
  }
@@ -2,6 +2,19 @@ import type { NextRequest } from 'next/server';
2
2
  import { NextResponse } from 'next/server';
3
3
  import type { MiddlewareCustomHandler } from '../types/types';
4
4
  export declare const localesSet: Set<string>;
5
+ /**
6
+ * This middleware function runs for every incoming request. Handles locale
7
+ * detection/routing, then optionally defers to your own custom logic.
8
+ *
9
+ * @param request The incoming request (pass through from your `middleware.ts`).
10
+ * @param options.middlewareHandler Your own logic (auth, feature flags, etc.),
11
+ * run alongside locale routing — see {@link MiddlewareCustomHandler} for the
12
+ * full contract (when it runs, what `targetUrl` means, what to return).
13
+ * @param options.runHandlerOnRedirect By default, `middlewareHandler` only
14
+ * runs when the library is NOT performing a locale redirect (i.e. on
15
+ * rewrite or `next()`). Set to `true` to also run it on redirects.
16
+ * Defaults to `false`.
17
+ */
5
18
  export default function intlMiddleware(request: NextRequest, options?: {
6
19
  middlewareHandler?: MiddlewareCustomHandler;
7
20
  runHandlerOnRedirect?: boolean;
@@ -19,7 +19,19 @@ async function getIsBotValue(userAgent) {
19
19
  }
20
20
  const getIsBotValueCache = cache(getIsBotValue);
21
21
  export const localesSet = new Set(config.locales);
22
- // This middleware function runs for every incoming request
22
+ /**
23
+ * This middleware function runs for every incoming request. Handles locale
24
+ * detection/routing, then optionally defers to your own custom logic.
25
+ *
26
+ * @param request The incoming request (pass through from your `middleware.ts`).
27
+ * @param options.middlewareHandler Your own logic (auth, feature flags, etc.),
28
+ * run alongside locale routing — see {@link MiddlewareCustomHandler} for the
29
+ * full contract (when it runs, what `targetUrl` means, what to return).
30
+ * @param options.runHandlerOnRedirect By default, `middlewareHandler` only
31
+ * runs when the library is NOT performing a locale redirect (i.e. on
32
+ * rewrite or `next()`). Set to `true` to also run it on redirects.
33
+ * Defaults to `false`.
34
+ */
23
35
  export default async function intlMiddleware(request, options) {
24
36
  try {
25
37
  let initialChosenLocale;
@@ -1,4 +1,8 @@
1
1
  "use strict";
2
+ // NOTE: currently disabled — this file exports nothing at runtime, even
3
+ // though "./getLayoutStates" is listed in package.json's exports map.
4
+ // Do not rely on `cloudflare-next-intl/getLayoutStates` until this is
5
+ // re-enabled; use `getLocale()` + read the theme cookie manually instead.
2
6
  // "use server";
3
7
  // import { cookies } from "next/headers";
4
8
  // import { localeCookieName, isDarkCookieKey } from "../config/cookie_key";
@@ -1,3 +1,28 @@
1
+ /**
2
+ * Builds the `alternates` field for Next's `generateMetadata` — a canonical
3
+ * URL plus per-locale `hreflang` links, exported memoized as `alternatesLinks`
4
+ * from `cloudflare-next-intl/metadata`.
5
+ *
6
+ * @param url Absolute base URL of your site (no locale, no path),
7
+ * e.g. `"https://example.com"`.
8
+ * @param locale The current page's locale — used to decide the
9
+ * `canonical` URL (only set for `defaultLocale` unless you pass one).
10
+ * @param linkPart The path segment after the locale, e.g. `"/about"`.
11
+ * Pass `"/"` or omit for the root page.
12
+ * @param canonical Optional override for the canonical URL.
13
+ * @returns `{ canonical, languages }` ready to spread into `Metadata.alternates`,
14
+ * or `undefined` if building the links threw (logged, not re-thrown).
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * export async function generateMetadata({ params }) {
19
+ * const { locale } = await params;
20
+ * return {
21
+ * alternates: alternatesLinks({ url: "https://example.com", locale, linkPart: "/about" }),
22
+ * };
23
+ * }
24
+ * ```
25
+ */
1
26
  export declare function iAlternatesLinks({ locale, url, canonical, linkPart }: {
2
27
  url: string;
3
28
  locale: string;
@@ -1,5 +1,30 @@
1
1
  import { cache } from "react";
2
2
  import config from "../config/intl_config";
3
+ /**
4
+ * Builds the `alternates` field for Next's `generateMetadata` — a canonical
5
+ * URL plus per-locale `hreflang` links, exported memoized as `alternatesLinks`
6
+ * from `cloudflare-next-intl/metadata`.
7
+ *
8
+ * @param url Absolute base URL of your site (no locale, no path),
9
+ * e.g. `"https://example.com"`.
10
+ * @param locale The current page's locale — used to decide the
11
+ * `canonical` URL (only set for `defaultLocale` unless you pass one).
12
+ * @param linkPart The path segment after the locale, e.g. `"/about"`.
13
+ * Pass `"/"` or omit for the root page.
14
+ * @param canonical Optional override for the canonical URL.
15
+ * @returns `{ canonical, languages }` ready to spread into `Metadata.alternates`,
16
+ * or `undefined` if building the links threw (logged, not re-thrown).
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * export async function generateMetadata({ params }) {
21
+ * const { locale } = await params;
22
+ * return {
23
+ * alternates: alternatesLinks({ url: "https://example.com", locale, linkPart: "/about" }),
24
+ * };
25
+ * }
26
+ * ```
27
+ */
3
28
  export function iAlternatesLinks({ locale, url, canonical, linkPart }) {
4
29
  try {
5
30
  const linkPartValue = linkPart == '/' ? undefined : linkPart;
@@ -1 +1,20 @@
1
+ /**
2
+ * Server component exported as `IntlHelperScript` from
3
+ * `cloudflare-next-intl/IntlHelperScript`. Renders inline bootstrap
4
+ * `<script>` tags that run before hydration to avoid FOUC/flicker:
5
+ * - syncs dark-mode class from the theme cookie (or `prefers-color-scheme`)
6
+ * - redirects to the locale-prefixed URL if the locale cookie disagrees
7
+ * with the current path (covers client-side navigation edge cases)
8
+ * - (prod only) checks `BUILD_ID` and force-reloads on stale deploys
9
+ *
10
+ * Place it once in your root layout's `<head>`, alongside `IntlProvider`.
11
+ * No props.
12
+ *
13
+ * @example
14
+ * ```tsx
15
+ * <head>
16
+ * <IntlHelperScript />
17
+ * </head>
18
+ * ```
19
+ */
1
20
  export default function HelperScript(): Component | null;
@@ -4,6 +4,25 @@ import config from "../../config/intl_config";
4
4
  import ClientHelperScript from "../../client/components/client_helper_script";
5
5
  const isDev = process.env.NODE_ENV === 'development';
6
6
  const secureCookieAttribute = isDev ? '+ " Secure;"' : '';
7
+ /**
8
+ * Server component exported as `IntlHelperScript` from
9
+ * `cloudflare-next-intl/IntlHelperScript`. Renders inline bootstrap
10
+ * `<script>` tags that run before hydration to avoid FOUC/flicker:
11
+ * - syncs dark-mode class from the theme cookie (or `prefers-color-scheme`)
12
+ * - redirects to the locale-prefixed URL if the locale cookie disagrees
13
+ * with the current path (covers client-side navigation edge cases)
14
+ * - (prod only) checks `BUILD_ID` and force-reloads on stale deploys
15
+ *
16
+ * Place it once in your root layout's `<head>`, alongside `IntlProvider`.
17
+ * No props.
18
+ *
19
+ * @example
20
+ * ```tsx
21
+ * <head>
22
+ * <IntlHelperScript />
23
+ * </head>
24
+ * ```
25
+ */
7
26
  export default function HelperScript() {
8
27
  return _jsxs(_Fragment, { children: [!isDev &&
9
28
  _jsx("script", { id: "build-id-script", children: `(async function() {
@@ -1,5 +1,25 @@
1
1
  import { type LinkProps } from 'next/link';
2
2
  import { type ComponentProps } from 'react';
3
3
  type NextLinkProps = Omit<ComponentProps<'a'>, keyof LinkProps> & Omit<LinkProps, 'locale'>;
4
+ /**
5
+ * Server-safe, locale-aware drop-in replacement for `next/link`. Import
6
+ * from `cloudflare-next-intl/Link` (a separate subpath from the client-side
7
+ * `LocaleLink`).
8
+ *
9
+ * Prepends the current locale segment to `href` automatically when the
10
+ * current locale isn't the `defaultLocale` — you never build the
11
+ * `/en/about`-style path yourself. To link to a SPECIFIC locale (a language
12
+ * switcher) use `LocaleLink` instead, which takes an explicit `locale` prop.
13
+ *
14
+ * All other `next/link` props (`prefetch`, `replace`, `scroll`, etc.) and
15
+ * standard `<a>` props are passed straight through.
16
+ *
17
+ * @example
18
+ * ```tsx
19
+ * import Link from "cloudflare-next-intl/Link";
20
+ *
21
+ * <Link href="/about">About</Link> // -> "/about" or "/de/about"
22
+ * ```
23
+ */
4
24
  declare const Link: import("react").ForwardRefExoticComponent<Omit<NextLinkProps, "ref"> & import("react").RefAttributes<HTMLAnchorElement>>;
5
25
  export default Link;
@@ -22,5 +22,25 @@ function CustomLinkFunction({ href, prefetch, ...rest }, ref) {
22
22
  }
23
23
  return _jsx(LinkComponent, { ref: ref, href: pathnames, prefetch: prefetch, ...rest });
24
24
  }
25
+ /**
26
+ * Server-safe, locale-aware drop-in replacement for `next/link`. Import
27
+ * from `cloudflare-next-intl/Link` (a separate subpath from the client-side
28
+ * `LocaleLink`).
29
+ *
30
+ * Prepends the current locale segment to `href` automatically when the
31
+ * current locale isn't the `defaultLocale` — you never build the
32
+ * `/en/about`-style path yourself. To link to a SPECIFIC locale (a language
33
+ * switcher) use `LocaleLink` instead, which takes an explicit `locale` prop.
34
+ *
35
+ * All other `next/link` props (`prefetch`, `replace`, `scroll`, etc.) and
36
+ * standard `<a>` props are passed straight through.
37
+ *
38
+ * @example
39
+ * ```tsx
40
+ * import Link from "cloudflare-next-intl/Link";
41
+ *
42
+ * <Link href="/about">About</Link> // -> "/about" or "/de/about"
43
+ * ```
44
+ */
25
45
  const Link = forwardRef(CustomLinkFunction);
26
46
  export default Link;
@@ -1,4 +1,33 @@
1
1
  import type { TranslationObject } from "../../types/types";
2
+ /**
3
+ * Server component that provides locale/messages context to the rest of the
4
+ * tree. Exported publicly as `IntlProvider` from `cloudflare-next-intl/serverProvider`.
5
+ *
6
+ * Wrap this around your app once, near the root layout, below `[locale]`.
7
+ * It seeds the server-side locale/message caches (for `getLocale`/`getTranslations`)
8
+ * and also passes them to the client `LocaleContext` (for `useLocale`/`useTranslations`
9
+ * in client components).
10
+ *
11
+ * @param language The current route's locale (typically the `[locale]` route
12
+ * param). Must be one of your configured `locales` — calls `notFound()`
13
+ * otherwise.
14
+ * @param messages Optional pre-loaded messages for `language`. If omitted,
15
+ * they're loaded via `getMessage(language)`.
16
+ *
17
+ * @example
18
+ * ```tsx
19
+ * export default async function RootLayout({ children, params }) {
20
+ * const { locale } = await params;
21
+ * return (
22
+ * <html lang={locale}>
23
+ * <body>
24
+ * <IntlProvider language={locale}>{children}</IntlProvider>
25
+ * </body>
26
+ * </html>
27
+ * );
28
+ * }
29
+ * ```
30
+ */
2
31
  export default function LocationzationProvider({ language, messages, children }: {
3
32
  language: string;
4
33
  messages?: TranslationObject;
@@ -4,6 +4,35 @@ import { getMessage } from "../functions/server";
4
4
  import dynamic from "next/dynamic";
5
5
  import { localesSet } from "../../config/middleware";
6
6
  const LocationzationClientProvider = dynamic(() => import("../../client/components/client_provider"));
7
+ /**
8
+ * Server component that provides locale/messages context to the rest of the
9
+ * tree. Exported publicly as `IntlProvider` from `cloudflare-next-intl/serverProvider`.
10
+ *
11
+ * Wrap this around your app once, near the root layout, below `[locale]`.
12
+ * It seeds the server-side locale/message caches (for `getLocale`/`getTranslations`)
13
+ * and also passes them to the client `LocaleContext` (for `useLocale`/`useTranslations`
14
+ * in client components).
15
+ *
16
+ * @param language The current route's locale (typically the `[locale]` route
17
+ * param). Must be one of your configured `locales` — calls `notFound()`
18
+ * otherwise.
19
+ * @param messages Optional pre-loaded messages for `language`. If omitted,
20
+ * they're loaded via `getMessage(language)`.
21
+ *
22
+ * @example
23
+ * ```tsx
24
+ * export default async function RootLayout({ children, params }) {
25
+ * const { locale } = await params;
26
+ * return (
27
+ * <html lang={locale}>
28
+ * <body>
29
+ * <IntlProvider language={locale}>{children}</IntlProvider>
30
+ * </body>
31
+ * </html>
32
+ * );
33
+ * }
34
+ * ```
35
+ */
7
36
  export default async function LocationzationProvider({ language, messages, children }) {
8
37
  if (!localesSet.has(language)) {
9
38
  const { notFound } = await import("next/navigation");
@@ -1,3 +1,15 @@
1
+ /**
2
+ * Generates the `[locale]` route params for every configured locale — pass
3
+ * directly as your `[locale]/layout.tsx`'s `generateStaticParams` so Next
4
+ * pre-renders/statically-generates a route for each locale.
5
+ *
6
+ * @returns One `{ locale }` object per entry in your `setIntlConfig({ locales })`.
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * export const generateStaticParams = getLocaleStaticParams;
11
+ * ```
12
+ */
1
13
  export declare function getLocaleStaticParams(): {
2
14
  locale: string;
3
15
  }[];
@@ -1,4 +1,16 @@
1
1
  import config from "../../config/intl_config";
2
+ /**
3
+ * Generates the `[locale]` route params for every configured locale — pass
4
+ * directly as your `[locale]/layout.tsx`'s `generateStaticParams` so Next
5
+ * pre-renders/statically-generates a route for each locale.
6
+ *
7
+ * @returns One `{ locale }` object per entry in your `setIntlConfig({ locales })`.
8
+ *
9
+ * @example
10
+ * ```tsx
11
+ * export const generateStaticParams = getLocaleStaticParams;
12
+ * ```
13
+ */
2
14
  export function getLocaleStaticParams() {
3
15
  return config.locales.map((locale) => ({ locale }));
4
16
  }
@@ -6,6 +6,14 @@ import type { TranslationObject, TranslatorReturnType } from "../../types/types"
6
6
  * @returns A promise that resolves to the TranslationObject for the given locale.
7
7
  */
8
8
  declare function iGetMessage(locale: string): Promise<TranslationObject>;
9
+ /**
10
+ * Server-only: loads (and caches) the translation messages for a locale.
11
+ * Use {@link getTranslations} instead unless you need the raw message object.
12
+ *
13
+ * @param locale The locale to load messages for (e.g. `"en"`).
14
+ * @returns The `TranslationObject` for that locale.
15
+ * @throws via `notFound()` if `locale` isn't in your configured locales.
16
+ */
9
17
  export declare const getMessage: typeof iGetMessage;
10
18
  /**
11
19
  * Retrieves a translation function for a specific namespace and locale.
@@ -15,6 +23,25 @@ export declare const getMessage: typeof iGetMessage;
15
23
  * @returns A promise that resolves to a function, which takes a key and returns the translated string.
16
24
  */
17
25
  declare function iGetTranslations(namespace: string, locale?: string): Promise<TranslatorReturnType>;
26
+ /**
27
+ * Server Component only: gets a translation function for a namespace.
28
+ *
29
+ * @param namespace Dot-separated key prefix into your messages file
30
+ * (e.g. `"HomePage"`, `"Common.buttons"`).
31
+ * @param locale Optional. Defaults to {@link getLocale}'s result — pass
32
+ * this explicitly only if you already resolved the locale (e.g. from route
33
+ * params) to avoid an extra lookup.
34
+ * @returns A function `(key: string) => string` that looks up `key` inside
35
+ * `namespace`.
36
+ *
37
+ * @example
38
+ * ```tsx
39
+ * export default async function Page() {
40
+ * const t = await getTranslations("HomePage");
41
+ * return <h1>{t("title")}</h1>;
42
+ * }
43
+ * ```
44
+ */
18
45
  export declare const getTranslations: typeof iGetTranslations;
19
46
  /**
20
47
  * Determines the current locale. It first checks for an explicitly set locale,
@@ -22,5 +49,19 @@ export declare const getTranslations: typeof iGetTranslations;
22
49
  * @returns A promise that resolves to the determined Language.
23
50
  */
24
51
  declare function iGetLocale(): Promise<string>;
52
+ /**
53
+ * Server Component only: resolves the current request's locale.
54
+ *
55
+ * Order of resolution: an explicitly-set locale (e.g. via `IntlProvider`)
56
+ * takes priority, then falls back to the `NEXT_LOCALE`-style cookie set by
57
+ * `intlMiddleware`, then to `defaultLocale` from your `setIntlConfig` config.
58
+ *
59
+ * @returns The resolved locale string (e.g. `"en"`).
60
+ *
61
+ * @example
62
+ * ```tsx
63
+ * const locale = await getLocale();
64
+ * ```
65
+ */
25
66
  export declare const getLocale: typeof iGetLocale;
26
67
  export {};
@@ -33,6 +33,14 @@ async function iGetMessage(locale) {
33
33
  return getMessageCache(locale); // Assert non-null because it's guaranteed to be in the map
34
34
  }
35
35
  }
36
+ /**
37
+ * Server-only: loads (and caches) the translation messages for a locale.
38
+ * Use {@link getTranslations} instead unless you need the raw message object.
39
+ *
40
+ * @param locale The locale to load messages for (e.g. `"en"`).
41
+ * @returns The `TranslationObject` for that locale.
42
+ * @throws via `notFound()` if `locale` isn't in your configured locales.
43
+ */
36
44
  export const getMessage = cache(iGetMessage);
37
45
  /**
38
46
  * Retrieves a translation function for a specific namespace and locale.
@@ -53,6 +61,25 @@ async function iGetTranslations(namespace, locale) {
53
61
  const serverMessages = await iGetMessage(effectiveLocale);
54
62
  return getTranslationsImpl(effectiveLocale, serverMessages, namespace, cacheKey);
55
63
  }
64
+ /**
65
+ * Server Component only: gets a translation function for a namespace.
66
+ *
67
+ * @param namespace Dot-separated key prefix into your messages file
68
+ * (e.g. `"HomePage"`, `"Common.buttons"`).
69
+ * @param locale Optional. Defaults to {@link getLocale}'s result — pass
70
+ * this explicitly only if you already resolved the locale (e.g. from route
71
+ * params) to avoid an extra lookup.
72
+ * @returns A function `(key: string) => string` that looks up `key` inside
73
+ * `namespace`.
74
+ *
75
+ * @example
76
+ * ```tsx
77
+ * export default async function Page() {
78
+ * const t = await getTranslations("HomePage");
79
+ * return <h1>{t("title")}</h1>;
80
+ * }
81
+ * ```
82
+ */
56
83
  export const getTranslations = cache(iGetTranslations);
57
84
  /**
58
85
  * Determines the current locale. It first checks for an explicitly set locale,
@@ -83,4 +110,18 @@ async function iGetLocale() {
83
110
  return config.defaultLocale;
84
111
  }
85
112
  }
113
+ /**
114
+ * Server Component only: resolves the current request's locale.
115
+ *
116
+ * Order of resolution: an explicitly-set locale (e.g. via `IntlProvider`)
117
+ * takes priority, then falls back to the `NEXT_LOCALE`-style cookie set by
118
+ * `intlMiddleware`, then to `defaultLocale` from your `setIntlConfig` config.
119
+ *
120
+ * @returns The resolved locale string (e.g. `"en"`).
121
+ *
122
+ * @example
123
+ * ```tsx
124
+ * const locale = await getLocale();
125
+ * ```
126
+ */
86
127
  export const getLocale = cache(iGetLocale);
@@ -1,6 +1,28 @@
1
1
  import type { TranslatorReturnType } from "../../types/types";
2
+ /**
3
+ * React Server Component `useLocale`, reached via the `cloudflare-next-intl/use`
4
+ * subpath's `react-server` condition (resolved automatically — you always
5
+ * just `import { useLocale } from "cloudflare-next-intl/use"`; the matching
6
+ * client-hook version from `client_hooks.ts` is used automatically in client
7
+ * components instead).
8
+ *
9
+ * Uses React's `use()` on the locale promise resolved by `getLocale()`/
10
+ * `IntlProvider` — must be called within a component tree wrapped in
11
+ * `IntlProvider`.
12
+ *
13
+ * @returns The current locale (e.g. `"en"`).
14
+ * @throws If called without an `IntlProvider` above it in the tree.
15
+ */
2
16
  export declare function useLocaleImpl(): string;
3
17
  export declare const useLocale: typeof useLocaleImpl;
18
+ /**
19
+ * React Server Component `useTranslations` — see {@link useLocaleImpl} for
20
+ * the `react-server`/client resolution note.
21
+ *
22
+ * @param namespace Dot-separated key prefix into your messages file.
23
+ * @returns A `(key: string) => string` translation function.
24
+ * @throws If called without an `IntlProvider` above it in the tree.
25
+ */
4
26
  declare function useTranslationsImpl(namespace: string): TranslatorReturnType;
5
27
  export declare const useTranslations: typeof useTranslationsImpl;
6
28
  export {};
@@ -1,6 +1,20 @@
1
1
  import { getTranslationsImpl } from "../../general/general_functions";
2
2
  import { getLocale, getMessage } from "./server";
3
3
  import { cache, use } from "react";
4
+ /**
5
+ * React Server Component `useLocale`, reached via the `cloudflare-next-intl/use`
6
+ * subpath's `react-server` condition (resolved automatically — you always
7
+ * just `import { useLocale } from "cloudflare-next-intl/use"`; the matching
8
+ * client-hook version from `client_hooks.ts` is used automatically in client
9
+ * components instead).
10
+ *
11
+ * Uses React's `use()` on the locale promise resolved by `getLocale()`/
12
+ * `IntlProvider` — must be called within a component tree wrapped in
13
+ * `IntlProvider`.
14
+ *
15
+ * @returns The current locale (e.g. `"en"`).
16
+ * @throws If called without an `IntlProvider` above it in the tree.
17
+ */
4
18
  export function useLocaleImpl() {
5
19
  const language = use(getLocale());
6
20
  if (language === undefined) {
@@ -9,6 +23,14 @@ export function useLocaleImpl() {
9
23
  return language;
10
24
  }
11
25
  export const useLocale = cache(useLocaleImpl);
26
+ /**
27
+ * React Server Component `useTranslations` — see {@link useLocaleImpl} for
28
+ * the `react-server`/client resolution note.
29
+ *
30
+ * @param namespace Dot-separated key prefix into your messages file.
31
+ * @returns A `(key: string) => string` translation function.
32
+ * @throws If called without an `IntlProvider` above it in the tree.
33
+ */
12
34
  function useTranslationsImpl(namespace) {
13
35
  const language = use(getLocale());
14
36
  const messages = use(getMessage(language));
@@ -1,3 +1,13 @@
1
+ /**
2
+ * Light/dark theme toggle button, exported as `ThemeSwitcher` from
3
+ * `cloudflare-next-intl/ThemeSwitcher`. Persists the choice via the
4
+ * theme cookie this package's `IntlHelperScript` reads on load (so the
5
+ * correct theme applies before hydration, no flash).
6
+ *
7
+ * @param lightLabelText Accessible label shown/used when in light mode.
8
+ * @param darkLabelText Accessible label shown/used when in dark mode.
9
+ * @param className Optional class applied to the underlying button.
10
+ */
1
11
  export default function ThemeSwticher(params: {
2
12
  className?: string;
3
13
  lightLabelText: string;
@@ -1,6 +1,16 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Moon, Sun } from "./icons";
3
3
  import ThemeSwticherButton from "./theme_switcher_button";
4
+ /**
5
+ * Light/dark theme toggle button, exported as `ThemeSwitcher` from
6
+ * `cloudflare-next-intl/ThemeSwitcher`. Persists the choice via the
7
+ * theme cookie this package's `IntlHelperScript` reads on load (so the
8
+ * correct theme applies before hydration, no flash).
9
+ *
10
+ * @param lightLabelText Accessible label shown/used when in light mode.
11
+ * @param darkLabelText Accessible label shown/used when in dark mode.
12
+ * @param className Optional class applied to the underlying button.
13
+ */
4
14
  export default function ThemeSwticher(params) {
5
15
  return _jsxs(ThemeSwticherButton, { ...params, children: [_jsx(Sun, { className: "transition-transform duration-500 ease-in-out" +
6
16
  " rotate-0 scale-100 opacity-100 cursor-pointer" + // Default (light mode) state
@@ -1,9 +1,64 @@
1
1
  import type { NextRequest, NextResponse } from 'next/server';
2
2
  import type { Languages } from 'next/dist/lib/metadata/types/alternative-urls-types';
3
3
  import type { Videos } from 'next/dist/lib/metadata/types/metadata-types';
4
+ /**
5
+ * Custom middleware hook, run by `intlMiddleware` for your own logic
6
+ * (e.g. auth, feature flags, A/B tests) — on top of the library's own
7
+ * locale routing (locale-prefix rewrite/redirect).
8
+ *
9
+ * Called AFTER the library has resolved the locale for this request, but
10
+ * BEFORE it builds its own `NextResponse`:
11
+ * - When `targetUrl` is `undefined`: the request already had a valid
12
+ * locale prefix (e.g. `/en/about`) — the library would otherwise call
13
+ * `NextResponse.next()`.
14
+ * - When `targetUrl` is set: the library resolved a locale-prefixed URL
15
+ * the request should be routed to (e.g. `/` -> `/en/`). By default the
16
+ * library still performs this rewrite/redirect itself — `targetUrl` is
17
+ * informational, so you can react to it (e.g. log it, add a header),
18
+ * NOT something you are required to apply yourself.
19
+ * - If `initialChosenLocale === defaultLocale`, the library rewrites to
20
+ * `targetUrl` (URL bar unchanged).
21
+ * - Otherwise, the library redirects to `targetUrl` (only when
22
+ * `runHandlerOnRedirect: true` is also passed — by default this
23
+ * handler does NOT run on redirects).
24
+ *
25
+ * @param request The incoming request.
26
+ * @param locale The resolved locale for this request (e.g. `"en"`).
27
+ * @param targetUrl The locale-prefixed URL the library will rewrite/redirect
28
+ * to, or `undefined` when no rewrite/redirect is needed.
29
+ * @returns - A `NextResponse` to fully REPLACE the library's default
30
+ * response (e.g. `NextResponse.redirect(...)` to send an
31
+ * unauthenticated user to `/login` instead).
32
+ * - `null` to keep the library's default response
33
+ * (rewrite/redirect/`next()`) as-is — this is the common case.
34
+ *
35
+ * @example
36
+ * ```ts
37
+ * middlewareHandler: (request, locale, targetUrl) => {
38
+ * const session = request.cookies.get("session")?.value;
39
+ * if (!session) {
40
+ * return NextResponse.redirect(new URL(`/${locale}/login`, request.url));
41
+ * }
42
+ * return null; // keep default locale routing
43
+ * }
44
+ * ```
45
+ */
4
46
  export type MiddlewareCustomHandler = (request: NextRequest, locale: string, targetUrl: URL | undefined) => NextResponse<unknown> | null | Promise<NextResponse<unknown> | null>;
47
+ /** Your app's list of supported locale codes, e.g. `["en", "de"] as const`. */
5
48
  export type Locales = readonly string[];
49
+ /**
50
+ * NOTE: currently unused by `intlMiddleware`'s actual routing logic (it
51
+ * always rewrites for `defaultLocale` and redirects otherwise) — reserved
52
+ * for future use. Setting `localePrefix` on {@link RoutingConfig} has no
53
+ * runtime effect yet.
54
+ */
6
55
  export type LocalePrefixMode = 'always' | 'as-needed' | 'never';
56
+ /**
57
+ * The config object you build with `setIntlConfig` and export from the file
58
+ * referenced by the `@intl-config` alias in `next.config` (see the package
59
+ * README's Setup section). Consumed internally by `intlMiddleware`,
60
+ * `getLocale`, `getTranslations`, and friends.
61
+ */
7
62
  export interface RoutingConfig<AppLocales extends Locales, AppLocalePrefixMode extends LocalePrefixMode> {
8
63
  /**
9
64
  * All available locales.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloudflare-next-intl",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "Optimized Next Intl Package Special for App Router and Cloudflare",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",