Closed amannn closed 1 month ago
The latest updates on your projects. Learn more about Vercel for Git ↗︎
Name | Status | Preview | Comments | Updated (UTC) |
---|---|---|---|---|
next-intl-docs | ✅ Ready (Inspect) | Visit Preview | 💬 Add feedback | Oct 1, 2024 3:25pm |
next-intl-example-app-router | ✅ Ready (Inspect) | Visit Preview | 💬 Add feedback | Oct 1, 2024 3:25pm |
next-intl-example-app-router-without-i18n-routing | ✅ Ready (Inspect) | Visit Preview | 💬 Add feedback | Oct 1, 2024 3:25pm |
@amannn
@DevOsaWebsite Thanks for providing feedback here!
Some questions:
Besides updating the version, did you upgrade to createNavigation? Are you referring to the markup resulting from the server side render, or the final markup after hydration that renders in the browser? Are you using domains in combination with localePrefix: 'as-needed'? Please refer to #1316, you should find an answer there.
Yes. You can see it here -- stackoverflow
About SSR (I am primarily a technical SEO + marketing specialist), SSR and avoidance of third-party side (use)Effects are important for me. Therefore, before writing a ticket, I checked with the eyes of a Google bot: in the Google browser console with the disabled JS, through View Page Source, and through the HTML validator. In all cases, there was a defaultLocale prefix in the DOM, which will create a lot of problems for multi-page sites.
As far as I understand, the prefix is important for the feature related to locale detection for "x-middleware-rewrite". Then I'm then I tried to turn it off
const handleI18nRouting = createMiddleware(routing, { localeDetection: false, });
but the problem is still there
then i went into the code and found the function
ClientLink.js
const finalLocale = locale || defaultLocale;
const prefix = utils.getLocalePrefix(finalLocale, localePrefix);
utils.js
function getLocalePrefix(locale, localePrefix) {
var _localePrefix$prefixe;
return localePrefix.mode !== 'never' && ((_localePrefix$prefixe = localePrefix.prefixes) === null || _localePrefix$prefixe === void 0 ? void 0 : _localePrefix$prefixe[locale]) ||
// We return a prefix even if `mode: 'never'`. It's up to the consumer
// to decide to use it or not.
'/' + locale;
}
locale || defaultLocale - no chance 😀😀😀
import {DEFAULT_LOCALE, LOCALES} from '@/config/locale';
import {type DomainsConfig} from 'next-intl/routing';
const locales = LOCALES as never as string[];
export const config: {
locales: string[];
defaultLocale: string;
localePrefix: 'always' | 'as-needed' | 'never';
domains?: DomainsConfig<string[]>;
} = {
locales,
defaultLocale: DEFAULT_LOCALE,
localePrefix: 'as-needed',
};
UPD p.s.
Before using next-intl - worked with custom i18n and encountered a similar problem, I solved it by off localeDetecting and reworked the function
const clearPathnameFromLocale = (pathname: string): string =>
pathname.replace(new RegExp(`^\\/(${LOCALES.join('|')})`), '');
const isIncludeLocale = (path: string): boolean =>
new RegExp(
`^\\/(${LOCALES.filter((locale) => locale !== DEFAULT_LOCALE).join('|')})(/|$)`,
).test(path);
const isExternalPath = (path: string): boolean =>
['http', 'https', 'tel:', 'mailto:', 'data:'].some((prefix) =>
path.includes(prefix),
);
const localizedPath = (
path: string,
locale: string = DEFAULT_LOCALE,
options?: {replaceLocaleIfDifferent?: boolean},
): string => {
const {replaceLocaleIfDifferent} = options || {};
if (
isExternalPath(path) ||
(!replaceLocaleIfDifferent &&
(locale === DEFAULT_LOCALE || isIncludeLocale(path)))
) {
return path;
}
if (replaceLocaleIfDifferent) {
return locale === DEFAULT_LOCALE
? clearPathnameFromLocale(path)
: `/${locale}${clearPathnameFromLocale(path)}`;
}
return `/${locale}${path.startsWith('/') ? path : `/${path}`}`;
};
some tests on the topic
it('should replace the locale if it is different and replaceLocaleIfDifferent is true', () => {
const path = '/pl/about';
const locale = 'en';
const expected = '/about';
const result = localizedPath(path, locale, {
replaceLocaleIfDifferent: true,
});
expect(result).toBe(expected);
});
it('should not replace the locale if it is different and replaceLocaleIfDifferent is false', () => {
const path = '/pl/about';
const locale = 'en';
const expected = '/pl/about';
const result = localizedPath(path, locale, {
replaceLocaleIfDifferent: false,
});
expect(result).toBe(expected);
});
to catch 404, added
src/app/[locale]/[...rest]/page.tsx
const RestPage = (): React.JSX.Element => {
const userAgent = headers().get('user-agent');
const xPathname = headers().get('x-pathname');
//TODO - add api service to log 404 pages
Logger.error(`404 Page not found: ${xPathname} / ${userAgent}`);
notFound();
};
@DevOsaWebsite
Thanks for including a link to your Stack Overflow question!
The reason why a prefix is rendered, is because you're linking to the default locale from a locale switcher (i.e. you're providing the locale
prop). In this case, it's expected to link to /en
, because if the user is on a secondary locale (that is prefixed) and would navigate to /
, the cookie would redirect the user back to the secondary locale.
Instead, in this case the user should navigate to /en
explicitly, update the cookie value and then go back to /
.
Here's a test for the relevant implementation:
If you want to avoid this, you could for example implement a locale switcher based on a select and use useRouter().push
instead (the App Router with i18n routing example does that).
Does this help? I should probably mention this in the docs …
This PR provides a new
createNavigation
function that supersedes the previously available APIs:createSharedPathnamesNavigation
createLocalizedPathnamesNavigation
This new function is a reimplementation of the existing navigation functionality that unifies the API for both use cases and also fixes a few quirks in the previous APIs.
Usage
(see the updated navigation docs)
Improvements
Link
can now be composed seamlessly into another component with itshref
prop without having to add a generic type argument.getPathname
is now available for both shared as well as localized pathnames (fixes https://github.com/amannn/next-intl/discussions/785)router.push
andredirect
now accept search params consistently via the object form (e.g.router.push({pathname: '/users', query: {sortBy: 'name'})
)—regardless of if you're using shared or localized pathnames. You can still userouter.push('/users?sortBy=name')
if you prefer though.localePrefix: 'as-needed'
, the initial render ofLink
now uses the correct pathname immediately during SSR (fixes #444). Previously, a prefix for the default locale was added during SSR and removed during hydration. Alsoredirect
now gets the final pathname right without having to add a superfluous prefix (fixes #1335). The only exception is when you uselocalePrefix: 'as-needed'
in combination withdomains
(see Special case: Usingdomains
withlocalePrefix: 'as-needed'
)createNavigation
weighs 2.98 kB (createSharedPathnamesNavigation
was 3.01 kB,createLocalizedPathnamesNavigation
was 3.27 kB)next-intl
for Next.js 15 to run without warnings.Migrating to
createNavigation
createNavigation
is generally considered a drop-in replacement, but a few changes might be necessary:createNavigation
is expected to receive your complete routing configuration. Ideally, you define this via thedefineRouting
function and pass the result tocreateNavigation
.createLocalizedPathnamesNavigation
and have composed theLink
with itshref
prop, you should no longer provide the genericPathname
type argument (see updated docs).redirect
, you now have to provide an explicit locale (even if it's just the current locale). The previously passed href (whether it was a string or an object) now needs to be wrapped in an object and assigned to thehref
prop. This change was necessary for an upcoming change in Next.js 15 whereheaders()
turns into a promise (see #1375 for details).