diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index d3f2d73f4..86d2cf7b7 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -7,7 +7,7 @@ on:
jobs:
build:
name: Build, lint, and test
- runs-on: ubuntu-latest
+ runs-on: macos-15
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
diff --git a/examples/example-app-router-migration/src/app/[locale]/layout.tsx b/examples/example-app-router-migration/src/app/[locale]/layout.tsx
index 5d3c87400..6864db0de 100644
--- a/examples/example-app-router-migration/src/app/[locale]/layout.tsx
+++ b/examples/example-app-router-migration/src/app/[locale]/layout.tsx
@@ -1,4 +1,6 @@
import {notFound} from 'next/navigation';
+import {NextIntlClientProvider} from 'next-intl';
+import {getMessages} from 'next-intl/server';
import {ReactNode} from 'react';
import {routing} from '@/i18n/routing';
@@ -13,12 +15,20 @@ export default async function LocaleLayout({children, params}: Props) {
notFound();
}
+ // Providing all messages to the client
+ // side is the easiest way to get started
+ const messages = await getMessages();
+
return (
next-intl
- {children}
+
+
+ {children}
+
+
);
}
diff --git a/examples/example-app-router-playground/src/components/MessagesTest.tsx b/examples/example-app-router-playground/src/components/MessagesTest.tsx
new file mode 100644
index 000000000..88f725260
--- /dev/null
+++ b/examples/example-app-router-playground/src/components/MessagesTest.tsx
@@ -0,0 +1,31 @@
+/* eslint-disable @typescript-eslint/no-unused-expressions */
+import {useMessages} from 'next-intl';
+import {getMessages} from 'next-intl/server';
+
+export async function GetMessages() {
+ const messages = await getMessages();
+
+ // Valid
+ messages.Index;
+ messages.Index.title;
+
+ // Invalid
+ // @ts-expect-error
+ messages.Unknown;
+ // @ts-expect-error
+ messages.Index.unknown;
+}
+
+export function UseMessages() {
+ const messages = useMessages();
+
+ // Valid
+ messages.Index;
+ messages.Index.title;
+
+ // Invalid
+ // @ts-expect-error
+ messages.Unknown;
+ // @ts-expect-error
+ messages.Index.unknown;
+}
diff --git a/packages/next-intl/src/server/react-server/getMessages.tsx b/packages/next-intl/src/server/react-server/getMessages.tsx
index 4129b1b93..39ac4ca0d 100644
--- a/packages/next-intl/src/server/react-server/getMessages.tsx
+++ b/packages/next-intl/src/server/react-server/getMessages.tsx
@@ -1,10 +1,10 @@
import {cache} from 'react';
-import type {AbstractIntlMessages} from 'use-intl';
+import type {useMessages as useMessagesType} from 'use-intl';
import getConfig from './getConfig.tsx';
export function getMessagesFromConfig(
config: Awaited>
-): AbstractIntlMessages {
+): ReturnType {
if (!config.messages) {
throw new Error(
'No messages found. Have you configured them correctly? See https://next-intl-docs.vercel.app/docs/configuration#messages'
@@ -19,8 +19,6 @@ async function getMessagesCachedImpl(locale?: string) {
}
const getMessagesCached = cache(getMessagesCachedImpl);
-export default async function getMessages(opts?: {
- locale?: string;
-}): Promise {
+export default async function getMessages(opts?: {locale?: string}) {
return getMessagesCached(opts?.locale);
}
diff --git a/packages/next-intl/src/server/react-server/index.test.tsx b/packages/next-intl/src/server/react-server/index.test.tsx
index 2f0d92f67..8f6054387 100644
--- a/packages/next-intl/src/server/react-server/index.test.tsx
+++ b/packages/next-intl/src/server/react-server/index.test.tsx
@@ -150,7 +150,7 @@ describe('getMessages', () => {
const messages = await getMessages();
// @ts-expect-error
- messages.about();
+ messages();
// Valid
return messages.about;
diff --git a/packages/use-intl/src/core/AbstractIntlMessages.tsx b/packages/use-intl/src/core/AbstractIntlMessages.tsx
index 42f02a333..3a62dec89 100644
--- a/packages/use-intl/src/core/AbstractIntlMessages.tsx
+++ b/packages/use-intl/src/core/AbstractIntlMessages.tsx
@@ -1,6 +1,7 @@
-/** A generic type that describes the shape of messages.
+/**
+ * A generic type that describes the shape of messages.
*
- * Optionally `IntlMessages` can be provided to get type safety for message
+ * Optionally, `IntlMessages` can be provided to get type safety for message
* namespaces and keys. See https://next-intl-docs.vercel.app/docs/usage/typescript
*/
type AbstractIntlMessages = {
diff --git a/packages/use-intl/src/react/useMessages.tsx b/packages/use-intl/src/react/useMessages.tsx
index 31a4bf48a..e921c4a0e 100644
--- a/packages/use-intl/src/react/useMessages.tsx
+++ b/packages/use-intl/src/react/useMessages.tsx
@@ -1,7 +1,6 @@
-import {AbstractIntlMessages} from '../core.tsx';
import useIntlContext from './useIntlContext.tsx';
-export default function useMessages(): AbstractIntlMessages {
+export default function useMessages(): IntlMessages {
const context = useIntlContext();
if (!context.messages) {