mirror of
https://github.com/Qortal/Qortal-Hub.git
synced 2025-06-06 00:16:58 +00:00
Add i18n and some languages
This commit is contained in:
parent
655c990917
commit
d2a82519ad
@ -21,4 +21,5 @@ Many additional details and a fully featured wiki will be created over time. Rea
|
||||
## Internationalization (i18n)
|
||||
|
||||
Qortal-Hub supports internationalization (i18n) using [i18next](https://www.i18next.com/), allowing seamless translation of UI text into multiple languages.
|
||||
The setup includes modularized translation files, language detection, and runtime language switching.
|
||||
The setup includes modularized translation files, language detection, context and runtime language switching.
|
||||
Files with translation are in `public/locales/<locale>` folder.
|
||||
|
48
i18n.js
Normal file
48
i18n.js
Normal file
@ -0,0 +1,48 @@
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
import HttpBackend from 'i18next-http-backend';
|
||||
import LocalStorageBackend from 'i18next-localstorage-backend';
|
||||
import HttpApi from 'i18next-http-backend';
|
||||
import i18n from 'i18next';
|
||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||
|
||||
// Detect environment
|
||||
const isDev = process.env.NODE_ENV === 'development';
|
||||
|
||||
// Register custom postProcessor: it capitalizes the first letter of a translation-
|
||||
// Usage:
|
||||
// t('greeting', { postProcess: 'capitalize' })
|
||||
const capitalize = {
|
||||
type: 'postProcessor',
|
||||
name: 'capitalize',
|
||||
process: (value, key, options, translator) => {
|
||||
return value.charAt(0).toUpperCase() + value.slice(1);
|
||||
},
|
||||
};
|
||||
|
||||
i18n
|
||||
.use(HttpApi)
|
||||
.use(LanguageDetector)
|
||||
.use(initReactI18next)
|
||||
.use(capitalize)
|
||||
.init({
|
||||
debug: isDev,
|
||||
fallbackLng: 'en',
|
||||
ns: ['auth', 'core'],
|
||||
supportedLngs: ['en', 'it', 'fr', 'es'],
|
||||
backend: {
|
||||
backends: [LocalStorageBackend, HttpBackend],
|
||||
backendOptions: [
|
||||
{
|
||||
expirationTime: 7 * 24 * 60 * 60 * 1000, // 7 days
|
||||
},
|
||||
{
|
||||
loadPath: '/locales/{{lng}}/{{ns}}.json',
|
||||
},
|
||||
],
|
||||
},
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
},
|
||||
});
|
||||
|
||||
export default i18n;
|
10
package-lock.json
generated
10
package-lock.json
generated
@ -59,6 +59,7 @@
|
||||
"i18next": "^25.0.1",
|
||||
"i18next-browser-languagedetector": "^8.0.5",
|
||||
"i18next-http-backend": "^3.0.2",
|
||||
"i18next-localstorage-backend": "^4.2.0",
|
||||
"jssha": "3.3.1",
|
||||
"lit": "^3.2.1",
|
||||
"lodash": "^4.17.21",
|
||||
@ -10846,6 +10847,15 @@
|
||||
"cross-fetch": "4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/i18next-localstorage-backend": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/i18next-localstorage-backend/-/i18next-localstorage-backend-4.2.0.tgz",
|
||||
"integrity": "sha512-vglEQF0AnLriX7dLA2drHnqAYzHxnLwWQzBDw8YxcIDjOvYZz5rvpal59Dq4In+IHNmGNM32YgF0TDjBT0fHmA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.22.15"
|
||||
}
|
||||
},
|
||||
"node_modules/iconv-corefoundation": {
|
||||
"version": "1.1.7",
|
||||
"resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz",
|
||||
|
@ -64,6 +64,7 @@
|
||||
"i18next": "^25.0.1",
|
||||
"i18next-browser-languagedetector": "^8.0.5",
|
||||
"i18next-http-backend": "^3.0.2",
|
||||
"i18next-localstorage-backend": "^4.2.0",
|
||||
"jssha": "3.3.1",
|
||||
"lit": "^3.2.1",
|
||||
"lodash": "^4.17.21",
|
||||
|
10
public/locales/en/auth.json
Normal file
10
public/locales/en/auth.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"account_many": "accounts",
|
||||
"account_one": "account",
|
||||
"advanced_users": "for advanced users",
|
||||
"create_account": "create account",
|
||||
"welcome": "Welcome to",
|
||||
"use_local_node": "use local node",
|
||||
"change_apikey": "change API key",
|
||||
"choose_custom_node": "choose custom node"
|
||||
}
|
9
public/locales/en/core.json
Normal file
9
public/locales/en/core.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"add": {
|
||||
"task": "Add task"
|
||||
},
|
||||
"cancel": "Cancel",
|
||||
"description": "Description" ,
|
||||
"save": "Save",
|
||||
"title": "Title"
|
||||
}
|
10
public/locales/it/auth.json
Normal file
10
public/locales/it/auth.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"account_many": "account",
|
||||
"account_one": "account",
|
||||
"advanced_users": "Per utenti avanzati",
|
||||
"change_apikey": "Cambia la chiave API",
|
||||
"choose_custom_node": "Scegli un nodo custom",
|
||||
"create_account": "crea un account",
|
||||
"use_local_node": "Usa nodo locale",
|
||||
"welcome": "Benvenuto in"
|
||||
}
|
9
public/locales/it/core.json
Normal file
9
public/locales/it/core.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"add": {
|
||||
"task": "Aggiungi compito"
|
||||
},
|
||||
"cancel": "Cancella",
|
||||
"description": "Descrizione" ,
|
||||
"save": "Salva",
|
||||
"title": "Titolo"
|
||||
}
|
@ -30,6 +30,7 @@ import { cleanUrl, gateways } from '../background';
|
||||
import { GlobalContext } from '../App';
|
||||
import Tooltip, { TooltipProps, tooltipClasses } from '@mui/material/Tooltip';
|
||||
import ThemeSelector from '../components/Theme/ThemeSelector';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const manifestData = {
|
||||
version: '0.5.3',
|
||||
@ -84,6 +85,7 @@ export const NotAuthenticated = ({
|
||||
React.useState(null);
|
||||
const { showTutorial, hasSeenGettingStarted } = useContext(GlobalContext);
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation('auth');
|
||||
|
||||
const importedApiKeyRef = useRef(null);
|
||||
const currentNodeRef = useRef(null);
|
||||
@ -183,6 +185,7 @@ export const NotAuthenticated = ({
|
||||
useEffect(() => {
|
||||
importedApiKeyRef.current = importedApiKey;
|
||||
}, [importedApiKey]);
|
||||
|
||||
useEffect(() => {
|
||||
currentNodeRef.current = currentNode;
|
||||
}, [currentNode]);
|
||||
@ -309,6 +312,7 @@ export const NotAuthenticated = ({
|
||||
} else if (currentNodeRef.current) {
|
||||
payload = currentNodeRef.current;
|
||||
}
|
||||
|
||||
let isValid = false;
|
||||
|
||||
const url = `${payload?.url}/admin/settings/localAuthBypassEnabled`;
|
||||
@ -402,6 +406,7 @@ export const NotAuthenticated = ({
|
||||
const addCustomNode = () => {
|
||||
setMode('add-node');
|
||||
};
|
||||
|
||||
const saveCustomNodes = (myNodes, isFullListOfNodes) => {
|
||||
let nodes = [...(myNodes || [])];
|
||||
if (!isFullListOfNodes && customNodeToSaveIndex !== null) {
|
||||
@ -455,7 +460,9 @@ export const NotAuthenticated = ({
|
||||
>
|
||||
<img src={Logo1Dark} className="base-image" />
|
||||
</div>
|
||||
|
||||
<Spacer height="30px" />
|
||||
|
||||
<TextP
|
||||
sx={{
|
||||
textAlign: 'center',
|
||||
@ -463,7 +470,7 @@ export const NotAuthenticated = ({
|
||||
fontSize: '18px',
|
||||
}}
|
||||
>
|
||||
WELCOME TO
|
||||
{t('auth:welcome', { postProcess: 'capitalize' })}
|
||||
<TextSpan
|
||||
sx={{
|
||||
fontSize: '18px',
|
||||
@ -504,13 +511,9 @@ export const NotAuthenticated = ({
|
||||
}
|
||||
>
|
||||
<CustomButton onClick={() => setExtstate('wallets')}>
|
||||
{/* <input {...getInputProps()} /> */}
|
||||
Accounts
|
||||
{t('auth:account_many', { postProcess: 'capitalize' })}
|
||||
</CustomButton>
|
||||
</HtmlTooltip>
|
||||
{/* <Tooltip title="Authenticate by importing your Qortal JSON file" arrow>
|
||||
<img src={Info} />
|
||||
</Tooltip> */}
|
||||
</Box>
|
||||
|
||||
<Spacer height="6px" />
|
||||
@ -565,10 +568,11 @@ export const NotAuthenticated = ({
|
||||
},
|
||||
}}
|
||||
>
|
||||
Create account
|
||||
{t('auth:create_account', { postProcess: 'capitalize' })}
|
||||
</CustomButton>
|
||||
</HtmlTooltip>
|
||||
</Box>
|
||||
|
||||
<Spacer height="15px" />
|
||||
|
||||
<Typography
|
||||
@ -579,6 +583,7 @@ export const NotAuthenticated = ({
|
||||
>
|
||||
{'Using node: '} {currentNode?.url}
|
||||
</Typography>
|
||||
|
||||
<>
|
||||
<Spacer height="15px" />
|
||||
<Box
|
||||
@ -603,7 +608,7 @@ export const NotAuthenticated = ({
|
||||
textDecoration: 'underline',
|
||||
}}
|
||||
>
|
||||
For advanced users
|
||||
{t('auth:advanced_users', { postProcess: 'capitalize' })}
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
|
@ -6,6 +6,7 @@ import { MessageQueueProvider } from './MessageQueueContext.tsx';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
import { ThemeProvider } from './components/Theme/ThemeContext.tsx';
|
||||
import { CssBaseline } from '@mui/material';
|
||||
import '../i18n';
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<>
|
||||
|
Loading…
x
Reference in New Issue
Block a user