Closed gustaveWPM closed 10 months ago
Great idea, I agree that we could add a documentation about this. It should be pretty straitghtforward to implement manually.
Great idea, I agree that we could add a documentation about this. It should be pretty straitghtforward to implement manually.
Would you please share the code implementation here, I'm really in need of it right now.
Would you please share the code implementation here, I'm really in need of it right now.
Ping @RemyJouni
I know that i18next integrates this feature. So... Maybe something like this?
(I simply extract the parts of the code that seem most relevant to analyze here. I think there are a lot of safety checks and additional features that could be avoided.)
export function getCleanedCode(code) {
if (code && code.indexOf('_') > 0) return code.replace('_', '-');
return code;
}
// * ... Too lazy to investigate about the "options" object, sorry
export function formatLanguageCode(code, options = {}) {
// http://www.iana.org/assignments/language-tags/language-tags.xhtml
if (typeof code === 'string' && code.indexOf('-') > -1) {
const specialCases = ['hans', 'hant', 'latn', 'cyrl', 'cans', 'mong', 'arab'];
let p = code.split('-');
if (options.lowerCaseLng) {
p = p.map((part) => part.toLowerCase());
} else if (p.length === 2) {
p[0] = p[0].toLowerCase();
p[1] = p[1].toUpperCase();
if (specialCases.indexOf(p[1].toLowerCase()) > -1) p[1] = capitalize(p[1].toLowerCase());
} else if (p.length === 3) {
p[0] = p[0].toLowerCase();
// if length 2 guess it's a country
if (p[1].length === 2) p[1] = p[1].toUpperCase();
if (p[0] !== 'sgn' && p[2].length === 2) p[2] = p[2].toUpperCase();
if (specialCases.indexOf(p[1].toLowerCase()) > -1) p[1] = capitalize(p[1].toLowerCase());
if (specialCases.indexOf(p[2].toLowerCase()) > -1) p[2] = capitalize(p[2].toLowerCase());
}
return p.join('-');
}
return options.cleanCode || options.lowerCaseLng ? code.toLowerCase() : code;
}
export function getLanguagePartFromCode(code) {
code = getCleanedCode(code);
if (!code || code.indexOf('-') < 0) return code;
const p = code.split('-');
return formatLanguageCode(p[0]);
}
export function dir(lng) {
const rtlLngs = [
'ar',
'shu',
'sqr',
'ssh',
'xaa',
'yhd',
'yud',
'aao',
'abh',
'abv',
'acm',
'acq',
'acw',
'acx',
'acy',
'adf',
'ads',
'aeb',
'aec',
'afb',
'ajp',
'apc',
'apd',
'arb',
'arq',
'ars',
'ary',
'arz',
'auz',
'avl',
'ayh',
'ayl',
'ayn',
'ayp',
'bbz',
'pga',
'he',
'iw',
'ps',
'pbt',
'pbu',
'pst',
'prp',
'prd',
'ug',
'ur',
'ydd',
'yds',
'yih',
'ji',
'yi',
'hbo',
'men',
'xmn',
'fa',
'jpr',
'peo',
'pes',
'prs',
'dv',
'sam',
'ckb'
];
return rtlLngs.indexOf(getLanguagePartFromCode(lng)) > -1 || lng.toLowerCase().indexOf('-arab') > 1
? 'rtl'
: 'ltr';
}
Then:
// * ... import dir from '...'
const PagesHtmlElement: FunctionComponent<HtmlElementProps> = ({ children, params }) => {
const locale = params.locale;
return (
<html lang={locale} dir={dir(locale)}>
<body>{children}</body>
</html>
)
See also: https://github.com/i18next/i18next/blob/master/src/i18next.js#L507 (the dir
function is at line 507)
Next Intl have implemented this with an another approach, using a hook.
You could take inspiration from it.
There are also NPM libs for this purpose.
Like this one (idk if it fits your needs, I haven't tested any of those libs yet): rtl-detect
We could also use Intl.Locale.prototype.getTextInfo
, that is built-in in Node.js and most browsers (except Firefox, somehow): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/getTextInfo
It returns an object with a direction
property that is either ltr
or rtl
:
// In the root layout
export default function Layout({ children, params: { locale } }: { children: ReactElement, params: { locale: string } }) => {
const dir = new Intl.Locale(locale).getTextInfo().direction
return (
<html lang={locale} dir={dir}>
<body>
{children}
</body>
</html>
)
(note that this doesn't work in Firefox, if your root layout is a client component)
Hi @QuiiBz
I currently encounter some edge cases with dir="rtl"
/ dir="ltr"
.
I would like to know: is there a onLocaleChange
event/hook?
Maybe I could make a new issue to request this feature, but I prefer to primarily ask it here since it is related to this issue too.
EDIT: huuummm, maybe I could just call useCurrentLocale
. I'm dumb. Sorry! Gonna try it.
'k. onLocaleChange
is pointless.
Here is a minimalist example of what I've done:
// LanguageSwitcher.tsx
'use client';
import { useChangeLocale, useCurrentLocale } from '@/i18n/client';
import { FunctionComponent, useEffect } from 'react';
interface LanguageSwitcherProps {}
const LanguageSwitcher: FunctionComponent<LanguageSwitcherProps> = () => {
const currentLocale = useCurrentLocale();
const changeLocale = useChangeLocale();
useEffect(() => {
if (currentLocale === 'some-rtl-locale') document.documentElement.dir = 'rtl';
else document.documentElement.dir = 'ltr';
}, [currentLocale]);
return (
<div className="flex flex-col gap-4">
<button onClick={() => changeLocale('en')}>EN</Button>
<button onClick={() => changeLocale('fr')}>FR</Button>
</div>
);
};
export default LanguageSwitcher;
Looks great. Do you want to send a PR to add https://github.com/QuiiBz/next-international/issues/220#issuecomment-1749140518 and your useEffect
above to the documentation?
Looks great. Do you want to send a PR to add #220 (comment) and your
useEffect
above to the documentation?
It would be nice if I could do a little PR for the occasion! :) I might take a bit of time though, I don't think I'll be able to do it before the weekend.
Hello @QuiiBz
I found a way to make work the solution you described in https://github.com/QuiiBz/next-international/issues/220#issuecomment-1749140518
Even on Firefox, even on client component.
Using this polyfill: https://github.com/brettz9/intl-locale-textinfo-polyfill
Like this:
import Locale from 'intl-locale-textinfo-polyfill';
const { direction: dir } = new Locale(locale).textInfo;
// * ...
return (
<html lang={locale} dir={dir}>
// * ...
However, there is a small issue with this polyfill, which requires an ugly workaround for now: https://github.com/brettz9/intl-locale-textinfo-polyfill/issues/1
Although there's a need to write this rather ugly Typescript definition file, I think it's interesting to use this polyfill, especially as its developer seems rather active: this issue should be solved within a reasonable time.
I'm thinking of using this as a basis for RTL/LTR handling documentation (with the polyfill). I'll create the PR tomorrow.
Looks great, thanks for the investigation!
Okay, I think the PR is done: https://github.com/QuiiBz/next-international/pull/273
I'm looking forward to read your review. :) (I'm somehow a messy person, and I need to proofread a lot before I'm satisfied with my work. Sorry for spamming.)
The author of 'intl-locale-textinfo-polyfill'
just fixed the ponyfill import issue https://github.com/brettz9/intl-locale-textinfo-polyfill/issues/1#issuecomment-1784211024
We don't need the ugly Typescript definition file workaround to use it anymore.
Oops... I don't see the documentation page, nor on mobile, nor on desktop.
Indeed, the page is present (https://next-international.vercel.app/docs/rtl-support) but not shown in the sidebar, even though it works locally.
Redeployed without the build cache and it's now showing as expected.
Awesome! Glad to have contributed a little
Hello there!
I think it would be a good idea to introduce the concept of "Direction" regarding the
dir
attribute of the<html>
tag on the pages, in order to adjust the reading direction according to the app's current language.However, I don't think it's necessary to integrate this feature directly into Next International. It seems to me that third-party libraries could handle this adequately.
Personally, I think it would be a good idea to document this and provide a solution. No more, no less.