-
-
Notifications
You must be signed in to change notification settings - Fork 764
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
Setting <html lang="..."> by default #20
Comments
Hi @kachkaev, I don't think that's a "workaround" - it seems like a reasonable solution to me. In the NextJs paradigm, if you need to access attributes on the How did you expect this to work otherwise? It might be good practice, but I don't think it belongs in the example as it adds unnecessary complexity - a We can definitely link to this issue from the README, though. |
I'm not sure, to be honest. Ideally, I would expect Perhaps, mentioning the importance of |
Added with 16610ca. |
Hi! Sorry for reopening this, I felt like it was a better idea.
|
@stephanemombuleau Please provide a reproducible example (repository). Once you provide that, I can attempt to help you. This is most likely user error as the approach above works just if you add it to the simple example, and serialisation of |
It's all good now. This works:
This gives an empty object like above:
|
This is my solution, use the import { lngFromReq } from 'next-i18next/dist/commonjs/utils';
export default class MyDocument extends Document<IDocumentProps> {
public static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx);
const lng = lngFromReq(ctx.req);
const additionalProps = {
isRTL: RTL_LANGS.some(currentLng => currentLng === lng),
lng,
};
return { ...initialProps, ...additionalProps };
}
public render() {
const { isRTL, lng } = this.props;
return (
<html lang={lng} dir={isRTL ? 'rtl' : 'ltr'}>
<Head>
</Head>
<body>
<Main />
<NextScript />
</body>
</html>
);
}
} |
@felixmosh Just wanted to say that there are cases where that will not work. We have a util called lng-from-req that performs some logic on the |
@isaachinman , I've updated my snippet, thanx |
@isaachinman, just wanted to ping you, since the time I've started using Looks that it related to that destructuring: const {
allLanguages,
defaultLanguage,
fallbackLng
} = req.i18n.options; Any idea why? |
@felixmosh In what cases is |
It reproduces here: https://github.com/felixmosh/next-18next-bug |
@felixmosh Several problems:
|
@isaachinman sorry, just run |
@isaachinman , I've found the root cause, looks like requests for static files ( const lng = ctx.req.i18n ? lngFromReq(ctx.req) : ctx.req.lng; Thanx for the help 🤩 |
Yes, those routes are ignored by default. But they shouldn't running |
I'm not sure :/ |
@felixmosh Thanks for being persistent on this one - it's a genuine issue. We're ignoring the I've created a very simple fix for this with #215 and will merge/release it soon. |
Thank you man! |
It seems <Html lang={this.props.__NEXT_DATA__.props.initialProps.initialLanguage}> I'm using
|
Looks like you are using an internal prop |
@felixmosh It's a relatively common pattern for better or worse. |
I just wanted to set the language because it affected my CSS in mobile view, making the page width moveable which was looking very bad (even when i set the view port tag of course) componentDidMount() {
// inject language attribute for html tag, use _document.js in the future
const htmlEl = document.getElementsByTagName('html');
if (htmlEl && htmlEl.length > 0) {
htmlEl[0].setAttribute('lang','en');
}
} this works perfectly since componentDidMount is called only on client side. |
What's the recommended way to do that now, with Next 9.2? I was already using a "Head" component which uses Also, I don't quite like the fact that this workaround removes the usage of Couldn't an option be made available from |
Also, |
Did you tried this approach? It works for me for long time, without relaying on any private api's |
Thanks, I actually ended up writing my own: import { NextPageContext } from 'next';
import Document, { DocumentProps, Head, Main, NextScript } from 'next/document';
import get from 'lodash.get';
import { LOCALE_FR, resolveBestCountryCodes } from '../utils/locale';
/**
* XXX Is only rendered on the server side and not on the client side
*
* Used to inject <html lang=""> tag
*
* See https://github.com/zeit/next.js/#custom-document
*/
class TFPDocument extends Document<Props> {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx);
const { req, res }: NextPageContext = ctx;
const bestCountryCodes = resolveBestCountryCodes(req, LOCALE_FR);
const lang = get(bestCountryCodes, '[0]', 'en').toLowerCase();
return { ...initialProps, lang };
}
render() {
return (
<html lang={this.props.lang}>
<Head />
<body>
<Main />
<NextScript />
</body>
</html>
);
}
}
type Props = {
lang: string
} & DocumentProps
export default TFPDocument; But mine is based on proprietary |
@Vadorequest it returns 'fr'. |
I've found out that So the code may look like: export default class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx);
const {
res: { locals },
} = ctx;
const additionalProps = {
languageDirection: locals.languageDirection,
language: locals.language,
};
return { ...initialProps, ...additionalProps };
}
render() {
const { languageDirection, language } = this.props;
return (
<html lang={language} dir={languageDirection}>
<Head />
<body>
<Main />
<NextScript />
</body>
</html>
);
}
} |
This comment has been minimized.
This comment has been minimized.
How do these solutions handle language changes? Do I have to setup edit: apparently, you have to do it manually: // Change <html>'s language on languageChanged event
if (typeof window !== undefined) {
NextI18NextInstance.i18n.on('languageChanged', function(lang) {
const html = document.querySelector('html');
if (html) html.setAttribute('lang', lang);
});
} |
@martpie Hm, that's an interesting point. I think we should pull that logic into the |
Sure, I can do that |
@felixmosh your solution does not seem to work anymore for me, specifically when I build the app
It works in development mode though, very weird. Edit: it seems to depend to the availability of res.locals, if it's not already defined both |
@eric-burel I still see the assignment on Try to debug it :] |
Yep the problem is exactly there, if res.locals is not defined this part is jumped. It is not assigned because |
It worked when we used a custom Express server but it has no reason to work now with a generic approach. Edit: if googlers have the same issue, a quickfix is to add this code in if (appContext.ctx && appContext.ctx.res && !appContext.ctx.res.locals) {
appContext.ctx.res.locals = {};
} |
Ok so a more robust code is as follow: // i18next-http-middleware is in charge of enhancing the req object
interface IncomingMessageWithI18n extends IncomingMessage {
language?: string;
i18n: any;
}
export const i18nPropsFromCtx = (
ctx: NextPageContext
): Partial<HtmlLanguageProps> => {
if (!(ctx && ctx.req && (ctx.req as IncomingMessageWithI18n).language))
return {};
const req = ctx.req as IncomingMessageWithI18n;
return {
lang: req.language,
dir: req.i18n && req.i18n.dir(req.language),
};
}; and in plain JS: export const i18nPropsFromCtx = (ctx) => {
if (!(ctx && ctx.req && ctx.req.language))
return {};
const req = ctx.req;
return {
lang: req.language,
dir: req.i18n && req.i18n.dir(req.language),
};
}; Use that in your custom MyDocument.getInitialProps = async (ctx) => {
const i18nDocumentProps = i18nPropsFromCtx(ctx);
return {
i18nDocumentProps,
};
}; and then finally: export default class MyDocument extends Document {
render() {
const { i18nDocumentProps } = this.props;
return (
<Html
{...i18nDocumentProps}
>
.... Thanks all for your fast answers! |
Hi @eric-burel -- awesome stuff! 👍 I'm fiddling with next.js (TS version) and next-i18next. Your code works for me, but I am not sure where to import the 1: Can you please post the full code with imports?
is there any recommended way? Thanks! 🙏 |
Yep the full code is here in our starter. IncomingMessage comes from node First, what is the precise purpose of "dir" and "lang" attributes? I wanted to set them for SEO purpose, but I must admit that I can't tell if you actually need to make them dynamic or not (maybe to have the right autocorrect language for instance?). Since Document is smth server-rendered, you'll probably need a side-effect with a direct DOM mutation. So like |
@eric-burel Thanks! Beside SEO I use
|
Does not seem like this works for the serverless beta version (currently testing 5.0.0-beta.4). Works when setting it the first time. When I change the language it is not reflected in the HTML lang. Does anyone have a solution for the new serverless beta version? |
You can use |
The new i18n features in NextJs v10 will handle this automatically. I plan to rewrite |
I'm wondering if there is any way to implicitly set the
lang
attribute to the html tag. This is a good practice, which improves SEO and a11y.Currently, my workaround is roughly this:
If there is no way to inject
lang
automatically, shall it be mentioned in the example or plugin docs?The text was updated successfully, but these errors were encountered: