From 746f86fa5a7187acb6f161266bfe09612917f0c8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=A1lvaro=20s?=
<50526113+alvvaro@users.noreply.github.com>
Date: Thu, 24 Oct 2024 19:21:34 +0200
Subject: [PATCH 1/2] feat: useLanguage sensor
---
README.md | 1 +
docs/useLanguage.md | 26 ++++++++++++++++++++++++++
src/index.ts | 1 +
src/misc/util.ts | 2 ++
src/useLanguage.ts | 34 ++++++++++++++++++++++++++++++++++
stories/useLanguage.story.tsx | 26 ++++++++++++++++++++++++++
tests/useLanguage.test.ts | 25 +++++++++++++++++++++++++
7 files changed, 115 insertions(+)
create mode 100644 docs/useLanguage.md
create mode 100644 src/useLanguage.ts
create mode 100644 stories/useLanguage.story.tsx
create mode 100644 tests/useLanguage.test.ts
diff --git a/README.md b/README.md
index cc1728fafd..640a006084 100644
--- a/README.md
+++ b/README.md
@@ -51,6 +51,7 @@
- [`useIdle`](./docs/useIdle.md) — tracks whether user is being inactive.
- [`useIntersection`](./docs/useIntersection.md) — tracks an HTML element's intersection. [![][img-demo]](https://streamich.github.io/react-use/?path=/story/sensors-useintersection--demo)
- [`useKey`](./docs/useKey.md), [`useKeyPress`](./docs/useKeyPress.md), [`useKeyboardJs`](./docs/useKeyboardJs.md), and [`useKeyPressEvent`](./docs/useKeyPressEvent.md) — track keys. [![][img-demo]](https://streamich.github.io/react-use/?path=/story/sensors-usekeypressevent--demo)
+ - [`useLanguage`](./docs/useLanguage.md) — tracks the language of the document.
- [`useLocation`](./docs/useLocation.md) and [`useSearchParam`](./docs/useSearchParam.md) — tracks page navigation bar location state.
- [`useLongPress`](./docs/useLongPress.md) — tracks long press gesture of some element.
- [`useMedia`](./docs/useMedia.md) — tracks state of a CSS media query. [![][img-demo]](https://streamich.github.io/react-use/?path=/story/sensors-usemedia--demo)
diff --git a/docs/useLanguage.md b/docs/useLanguage.md
new file mode 100644
index 0000000000..c9acc90cf8
--- /dev/null
+++ b/docs/useLanguage.md
@@ -0,0 +1,26 @@
+# `useLanguage`
+
+React state hook that tracks the document's `lang` attribute.
+
+## Usage
+
+```jsx
+import { useLanguage } from 'react-use';
+
+const Demo = () => {
+ const [lang, setLang] = useLanguage();
+
+ return (
+
+
+ The document's current language is: {lang}
+
+
+
+
+
+
+
+ );
+};
+```
diff --git a/src/index.ts b/src/index.ts
index 62b69356b7..42c7554e9c 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -42,6 +42,7 @@ export { default as createBreakpoint } from './factory/createBreakpoint';
// export { default as useKeyboardJs } from './useKeyboardJs';
export { default as useKeyPress } from './useKeyPress';
export { default as useKeyPressEvent } from './useKeyPressEvent';
+export { default as useLanguage } from './useLanguage';
export { default as useLatest } from './useLatest';
export { default as useLifecycles } from './useLifecycles';
export { default as useList } from './useList';
diff --git a/src/misc/util.ts b/src/misc/util.ts
index 34fa42c206..34150b5503 100644
--- a/src/misc/util.ts
+++ b/src/misc/util.ts
@@ -21,3 +21,5 @@ export function off(
export const isBrowser = typeof window !== 'undefined';
export const isNavigator = typeof navigator !== 'undefined';
+
+export const isDocument = typeof document !== 'undefined';
diff --git a/src/useLanguage.ts b/src/useLanguage.ts
new file mode 100644
index 0000000000..c6ef77ff81
--- /dev/null
+++ b/src/useLanguage.ts
@@ -0,0 +1,34 @@
+import { useEffect, useState, Dispatch, SetStateAction } from 'react';
+import { isDocument } from './misc/util';
+
+function useLanguage() {
+ const [lang, setLangState] = useState(document.documentElement.lang);
+
+ const setLang: Dispatch> = (action) => {
+ if (typeof action === 'function') {
+ document.documentElement.lang = action(lang);
+ } else {
+ document.documentElement.lang = action;
+ }
+ };
+
+ useEffect(() => {
+ const handler = () => {
+ setLangState(document.documentElement.lang);
+ };
+
+ const observer = new MutationObserver(handler);
+ observer.observe(document.documentElement, {
+ attributes: true,
+ attributeFilter: ['lang'],
+ });
+
+ return () => observer.disconnect();
+ }, []);
+
+ return [lang, setLang] as const;
+}
+
+export default typeof isDocument
+ ? useLanguage
+ : () => ['', (_action: SetStateAction) => {}] as const;
diff --git a/stories/useLanguage.story.tsx b/stories/useLanguage.story.tsx
new file mode 100644
index 0000000000..ded4097cfe
--- /dev/null
+++ b/stories/useLanguage.story.tsx
@@ -0,0 +1,26 @@
+import { storiesOf } from '@storybook/react';
+import * as React from 'react';
+import { useLanguage } from '../src';
+import NewTabStory from './util/NewTabStory';
+import ShowDocs from './util/ShowDocs';
+
+const Demo = () => {
+ const [lang, setLang] = useLanguage();
+
+ return (
+
+
+
The document's current language is: {lang}
+
+
+
+
+
+
+
+ );
+};
+
+storiesOf('Sensors/useLanguage', module)
+ .add('Docs', () => )
+ .add('Demo', () => );
diff --git a/tests/useLanguage.test.ts b/tests/useLanguage.test.ts
new file mode 100644
index 0000000000..dade9ca187
--- /dev/null
+++ b/tests/useLanguage.test.ts
@@ -0,0 +1,25 @@
+import { renderHook } from '@testing-library/react-hooks';
+import useLanguage from '../src/useLanguage';
+
+describe('useLanguage', () => {
+ it('should be defined', () => {
+ expect(useLanguage).toBeDefined();
+ });
+
+ it('should retrieve the document language', () => {
+ document.documentElement.lang = 'ar-SA';
+ const hook = renderHook(() => useLanguage());
+
+ expect(document.documentElement.lang).toBe(hook.result.current[0]);
+ });
+
+ it('should update document language', () => {
+ const hook = renderHook(() => useLanguage());
+
+ hook.result.current[1]('bn-BD');
+ expect(document.documentElement.lang).toBe('bn-BD');
+
+ hook.result.current[1]('bn-IN');
+ expect(document.documentElement.lang).toBe('bn-IN');
+ });
+});
From 043fa5d332e70e93da0b643ec8c829c6eb0b3e07 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=A1lvaro=20s?=
<50526113+alvvaro@users.noreply.github.com>
Date: Thu, 24 Oct 2024 19:26:39 +0200
Subject: [PATCH 2/2] feat: useLanguage sensor
---
src/useLanguage.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/useLanguage.ts b/src/useLanguage.ts
index c6ef77ff81..4b793e0e39 100644
--- a/src/useLanguage.ts
+++ b/src/useLanguage.ts
@@ -29,6 +29,6 @@ function useLanguage() {
return [lang, setLang] as const;
}
-export default typeof isDocument
+export default isDocument
? useLanguage
: () => ['', (_action: SetStateAction) => {}] as const;