From f2bcc667e4852fa305f5e8beb7202bfe64e50b8e Mon Sep 17 00:00:00 2001
From: mntone <901816+mntone@users.noreply.github.com>
Date: Thu, 20 Jun 2024 14:02:14 +0900
Subject: [PATCH] Improves internationalization loading.
---
src/app/App.tsx | 30 +--------
src/index.css | 11 ++++
src/index.tsx | 5 +-
.../core/components/IntlLoader/index.tsx | 62 +++++++++++++++++++
src/modules/core/utils/language.ts | 10 ++-
5 files changed, 87 insertions(+), 31 deletions(-)
create mode 100644 src/modules/core/components/IntlLoader/index.tsx
diff --git a/src/app/App.tsx b/src/app/App.tsx
index 1c125d2..5416d0a 100644
--- a/src/app/App.tsx
+++ b/src/app/App.tsx
@@ -1,42 +1,16 @@
import './App.css'
-import { useEffect, useState } from 'react'
-import { IntlProvider } from 'react-intl'
-
-import { useEnvironment } from '@/core/components/EnvironmentProvider'
import { useBodyClass } from '@/core/hooks/bodyclass'
-import { loadLocale } from '@/core/utils/language'
import NotificationController from '@/notification/components/NotificationController'
import OverlayController from '@/overlay/components/OverlayController'
import OverlayHost from '@/overlay/components/OverlayHost'
import SettingsWindow from '@/settings'
-import { useAppSelector } from './hooks'
-
const App = () => {
- const [messages, setMessages] = useState(null)
-
useBodyClass()
- const environment = useEnvironment()
- const userLanguage = environment?.lang ?? 'en'
- const lang = useAppSelector(state => state.config.language) ?? userLanguage
- useEffect(() => {
- const fetchMessages = async () => {
- const messages = await loadLocale(lang)
- document.documentElement.setAttribute('lang', lang)
- setMessages(messages)
- }
- if (lang === 'en') {
- document.documentElement.setAttribute('lang', 'en')
- setMessages(null)
- } else {
- fetchMessages()
- }
- }, [lang])
-
return (
-
+ <>
@@ -49,7 +23,7 @@ const App = () => {
ShakeStreamKit. Copyright © 2024 mntone. Licensed under the GPLv3 license.
-
+ >
)
}
diff --git a/src/index.css b/src/index.css
index 58760c0..49bd232 100644
--- a/src/index.css
+++ b/src/index.css
@@ -58,6 +58,17 @@ html:lang(zh-TW) body, span:lang(zh-TW) {
font-family: Oswald, 'PingFang TC', STHeitiTC, 'Heiti TC', '黒体-繁', 'Microsoft JhengHei', sans-serif, 'Apple Color Emoji', 'Segoe UI Symbol';
}
+.Message {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 100vh;
+ color: var(--fg-tertiary);
+ background-color: #000;
+ font-size: 200%;
+ user-select: none;
+}
+
input,
input::-webkit-file-upload-button,
input::-webkit-slider-thumb,
diff --git a/src/index.tsx b/src/index.tsx
index a1ca8a7..8558662 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -7,6 +7,7 @@ import { createRoot } from 'react-dom/client'
import { PersistGate } from 'redux-persist/integration/react'
import EnvironmentProvider from '@/core/components/EnvironmentProvider'
+import IntlLoader from '@/core/components/IntlLoader'
import WebSocketProvider from '@/telemetry/components/WebSocketProvider'
import App from 'app/App'
@@ -24,7 +25,9 @@ if (element) {
>
-
+
+
+
diff --git a/src/modules/core/components/IntlLoader/index.tsx b/src/modules/core/components/IntlLoader/index.tsx
new file mode 100644
index 0000000..96ab73a
--- /dev/null
+++ b/src/modules/core/components/IntlLoader/index.tsx
@@ -0,0 +1,62 @@
+import { type PropsWithChildren, useState, useLayoutEffect } from 'react'
+import { IntlProvider } from 'react-intl'
+
+import { useAppSelector } from 'app/hooks'
+
+import { loadLocale } from '../../utils/language'
+import { useEnvironment } from '../EnvironmentProvider'
+
+const IntlLoader = ({ children }: PropsWithChildren) => {
+ const [messages, setMessages] = useState(null)
+
+ const userLanguage = useEnvironment()?.lang ?? 'en'
+ const language = useAppSelector(state => state.config.language) ?? userLanguage
+ useLayoutEffect(() => {
+ const controller = new AbortController()
+ const fetchMessages = async () => {
+ try {
+ const messages = await loadLocale(language, controller.signal)
+ document.documentElement.setAttribute('lang', language)
+ setMessages(messages)
+ } catch (err) {
+ if (err instanceof DOMException) {
+ if (err.name !== 'AbortError') {
+ console.log(err)
+ }
+ } else {
+ console.log(err)
+ }
+ }
+ }
+ if (language === 'en') {
+ document.documentElement.setAttribute('lang', 'en')
+ setMessages(null)
+ } else {
+ fetchMessages()
+ }
+
+ return () => {
+ controller.abort()
+ }
+ }, [language])
+
+ if (language !== 'en' && messages === null) {
+ return (
+
+ Loading…
+
+ )
+ }
+
+ return (
+
+ {children}
+
+ )
+}
+
+export default IntlLoader
diff --git a/src/modules/core/utils/language.ts b/src/modules/core/utils/language.ts
index c0b1333..baece60 100644
--- a/src/modules/core/utils/language.ts
+++ b/src/modules/core/utils/language.ts
@@ -27,8 +27,14 @@ export const detectLanguage = () => {
return language
}
-export const loadLocale = async (locale: string) => {
- const res = await fetch(`locales/${locale}.json`)
+export const loadLocale = async (locale: string, signal?: AbortSignal) => {
+ const res = await fetch(`locales/${locale}.json`, {
+ signal,
+ })
+ if (!res.ok) {
+ throw Error('Response is not OK')
+ }
+
const json = await res.json()
return json
}