Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(RSC): Various fixes #623

Merged
merged 3 commits into from
Nov 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion packages/next-intl/src/navigation/BaseLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,14 @@ function BaseLink<Locales extends AllLocales>(
ref: Props<Locales>['ref']
) {
const defaultLocale = useLocale();
const linkLocale = locale || defaultLocale;
return (
<BaseLinkWithLocale ref={ref} locale={locale || defaultLocale} {...rest} />
<BaseLinkWithLocale
ref={ref}
hrefLang={linkLocale}
locale={linkLocale}
{...rest}
/>
);
}

Expand Down
12 changes: 11 additions & 1 deletion packages/next-intl/src/navigation/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,9 @@ export function compileLocalizedPathname<Locales extends AllLocales, Pathname>({
function compilePath(
namedPath: Pathnames<Locales>[keyof Pathnames<Locales>]
) {
let compiled =
const template =
typeof namedPath === 'string' ? namedPath : namedPath[locale];
let compiled = template;

if (params) {
Object.entries(params).forEach(([key, value]) => {
Expand All @@ -122,6 +123,15 @@ export function compileLocalizedPathname<Locales extends AllLocales, Pathname>({
});
}

if (process.env.NODE_ENV !== 'production' && compiled.includes('[')) {
// Next.js throws anyway, therefore better provide a more helpful error message
throw new Error(
`Insufficient params provided for localized pathname.\nTemplate: ${template}\nParams: ${JSON.stringify(
params
)}`
);
}

if (query) {
compiled += serializeSearchParams(query);
}
Expand Down
4 changes: 2 additions & 2 deletions packages/next-intl/src/server/getFormatter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const getFormatterImpl = cache(async (locale: string) => {
* The formatter automatically receives the request config, but
* you can override it by passing in additional options.
*/
export default async function getFormatter(opts?: {locale?: string} | string) {
const locale = await resolveLocaleArg('getFormatter', opts);
export default async function getFormatter(opts?: {locale?: string}) {
const locale = await resolveLocaleArg(opts);
return getFormatterImpl(locale);
}
4 changes: 2 additions & 2 deletions packages/next-intl/src/server/getMessages.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const getMessagesImpl = cache(async (locale: string) => {
return config.messages;
});

export default async function getMessages(opts?: {locale?: string} | string) {
const locale = await resolveLocaleArg('getMessages', opts);
export default async function getMessages(opts?: {locale?: string}) {
const locale = await resolveLocaleArg(opts);
return getMessagesImpl(locale);
}
4 changes: 2 additions & 2 deletions packages/next-intl/src/server/getNow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const getNowImpl = cache(async (locale: string) => {
return config.now;
});

export default async function getNow(opts?: {locale?: string} | string) {
const locale = await resolveLocaleArg('getNow', opts);
export default async function getNow(opts?: {locale?: string}) {
const locale = await resolveLocaleArg(opts);
return getNowImpl(locale);
}
4 changes: 2 additions & 2 deletions packages/next-intl/src/server/getTimeZone.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const getTimeZoneImpl = cache(async (locale: string) => {
return config.timeZone;
});

export default async function getTimeZone(opts?: {locale?: string} | string) {
const locale = await resolveLocaleArg('getTimeZone', opts);
export default async function getTimeZone(opts?: {locale?: string}) {
const locale = await resolveLocaleArg(opts);
return getTimeZoneImpl(locale);
}
124 changes: 0 additions & 124 deletions packages/next-intl/src/server/getTranslator.tsx

This file was deleted.

3 changes: 0 additions & 3 deletions packages/next-intl/src/server/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,3 @@ export {default as getMessages} from './getMessages';
export {default as getLocale} from './getLocale';

export {setRequestLocale as unstable_setRequestLocale} from './RequestLocale';

// TODO: Remove
export {default as getTranslator} from './getTranslator';
33 changes: 7 additions & 26 deletions packages/next-intl/src/server/resolveLocaleArg.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,11 @@
import {cache} from 'react';
import getLocale from './getLocale';

// TODO: Remove

const deprecate = cache((fnName: string, locale: string) => {
console.error(
`\nDEPRECATION WARNING: Passing a locale as a string to \`${fnName}\` has been deprecated in favor of passing an object with a \`locale\` property instead:

${fnName}({locale: '${locale}'});

See https://github.com/amannn/next-intl/pull/600\n`
);
});

export default function resolveLocaleArg(
fnName: string,
optsOrDeprecatedLocale?: {locale?: string} | string
) {
if (typeof optsOrDeprecatedLocale === 'string') {
deprecate(fnName, optsOrDeprecatedLocale);
return optsOrDeprecatedLocale;
export default function resolveLocaleArg(opts?: {
locale?: string;
}): Promise<string> {
if (opts?.locale) {
return Promise.resolve(opts.locale);
} else {
return getLocale();
}

if (optsOrDeprecatedLocale?.locale) {
return optsOrDeprecatedLocale.locale;
}

return getLocale();
}
7 changes: 7 additions & 0 deletions packages/next-intl/test/navigation/BaseLink.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,13 @@ describe('unprefixed routing', () => {

expect(ref).toBeDefined();
});

it('sets an hreflang', () => {
render(<BaseLink href="/test">Test</BaseLink>);
expect(
screen.getByRole('link', {name: 'Test'}).getAttribute('hreflang')
).toBe('en');
});
});

describe('prefixed routing', () => {
Expand Down
26 changes: 25 additions & 1 deletion packages/next-intl/test/navigation/utils.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import {describe, expect, it} from 'vitest';
import {serializeSearchParams} from '../../src/navigation/utils';
import {
compileLocalizedPathname,
serializeSearchParams
} from '../../src/navigation/utils';

describe('serializeSearchParams', () => {
it('handles strings', () => {
Expand All @@ -18,3 +21,24 @@ describe('serializeSearchParams', () => {
expect(serializeSearchParams({v: ['a', 'b']})).toEqual('?v=a&v=b');
});
});

describe('compileLocalizedPathname', () => {
it('throws when params were not resolved', () => {
const locales: ReadonlyArray<string> = ['en'];
expect(() =>
// @ts-expect-error -- Purposefully miss a param
compileLocalizedPathname<typeof locales, '/test/[one]/[two]'>({
locale: 'en',
pathname: '/test/[one]/[two]',
pathnames: '/test/[one]/[two]',
params: {one: '1'}
})
).toThrow(
[
'Insufficient params provided for localized pathname.',
'Template: /test/[one]/[two]',
'Params: {"one":"1"}'
].join('\n')
);
});
});
Loading