Add i18n files and set first translations

This commit is contained in:
Nicola Benaglia 2025-04-21 13:38:31 +02:00
parent d2a82519ad
commit 3917aef452
8 changed files with 45 additions and 28 deletions

View File

@ -23,3 +23,5 @@ Many additional details and a fully featured wiki will be created over time. Rea
Qortal-Hub supports internationalization (i18n) using [i18next](https://www.i18next.com/), allowing seamless translation of UI text into multiple languages. 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, context 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. Files with translation are in `public/locales/<locale>` folder.
See [guidelines](./docs/i18n_languages.md).

10
docs/i18n_languages.md Normal file
View File

@ -0,0 +1,10 @@
# I18N Guidelines
In JSON file:
- Keep the file sorted
- Always write in lowercase
In GUI:
- If the first letter of the translation must be uppercase, use the postProcess, for example: `{t_auth('advanced_users', { postProcess: 'capitalize' })}`

View File

@ -28,7 +28,7 @@ i18n
debug: isDev, debug: isDev,
fallbackLng: 'en', fallbackLng: 'en',
ns: ['auth', 'core'], ns: ['auth', 'core'],
supportedLngs: ['en', 'it', 'fr', 'es'], supportedLngs: ['en', 'it'],
backend: { backend: {
backends: [LocalStorageBackend, HttpBackend], backends: [LocalStorageBackend, HttpBackend],
backendOptions: [ backendOptions: [

View File

@ -2,9 +2,12 @@
"account_many": "accounts", "account_many": "accounts",
"account_one": "account", "account_one": "account",
"advanced_users": "for advanced users", "advanced_users": "for advanced users",
"build_version": "build version",
"change_apikey": "change APIkey",
"choose_custom_node": "choose custom node",
"create_account": "create account", "create_account": "create account",
"welcome": "Welcome to", "import_key": "import APIkey",
"use_local_node": "use local node", "use_local_node": "use local node",
"change_apikey": "change API key", "using_node": "using node",
"choose_custom_node": "choose custom node" "welcome": "welcome to"
} }

View File

@ -1,9 +1,7 @@
{ {
"add": { "cancel": "cancel",
"task": "Add task" "choose": "choose",
}, "description": "description",
"cancel": "Cancel", "save": "save",
"description": "Description" , "title": "title"
"save": "Save",
"title": "Title"
} }

View File

@ -1,10 +1,13 @@
{ {
"account_many": "account", "account_many": "account",
"account_one": "account", "account_one": "account",
"advanced_users": "Per utenti avanzati", "advanced_users": "per utenti avanzati",
"change_apikey": "Cambia la chiave API", "build_version": "versione build",
"choose_custom_node": "Scegli un nodo custom", "change_apikey": "cambia la chiave API",
"choose_custom_node": "scegli un nodo custom",
"create_account": "crea un account", "create_account": "crea un account",
"use_local_node": "Usa nodo locale", "import_key": "importa chiave API",
"welcome": "Benvenuto in" "use_local_node": "usa nodo locale",
"using_node": "nodo in uso",
"welcome": "benvenuto in"
} }

View File

@ -1,9 +1,7 @@
{ {
"add": { "cancel": "cancella",
"task": "Aggiungi compito" "choose": "scegli",
}, "description": "descrizione",
"cancel": "Cancella", "save": "salva",
"description": "Descrizione" , "title": "titolo"
"save": "Salva",
"title": "Titolo"
} }

View File

@ -47,6 +47,7 @@ export const HtmlTooltip = styled(({ className, ...props }: TooltipProps) => (
fontSize: theme.typography.pxToRem(12), fontSize: theme.typography.pxToRem(12),
}, },
})); }));
function removeTrailingSlash(url) { function removeTrailingSlash(url) {
return url.replace(/\/+$/, ''); return url.replace(/\/+$/, '');
} }
@ -85,7 +86,7 @@ export const NotAuthenticated = ({
React.useState(null); React.useState(null);
const { showTutorial, hasSeenGettingStarted } = useContext(GlobalContext); const { showTutorial, hasSeenGettingStarted } = useContext(GlobalContext);
const theme = useTheme(); const theme = useTheme();
const { t } = useTranslation('auth'); const { t } = useTranslation(['auth', 'core']);
const importedApiKeyRef = useRef(null); const importedApiKeyRef = useRef(null);
const currentNodeRef = useRef(null); const currentNodeRef = useRef(null);
@ -581,7 +582,8 @@ export const NotAuthenticated = ({
visibility: !useLocalNode && 'hidden', visibility: !useLocalNode && 'hidden',
}} }}
> >
{'Using node: '} {currentNode?.url} {t('auth:using_node', { postProcess: 'capitalize' })}:{' '}
{currentNode?.url}
</Typography> </Typography>
<> <>
@ -693,7 +695,7 @@ export const NotAuthenticated = ({
variant="contained" variant="contained"
component="label" component="label"
> >
Choose custom node {t('auth:choose_custom_node', { postProcess: 'capitalize' })}
</Button> </Button>
</> </>
<Typography <Typography
@ -702,7 +704,8 @@ export const NotAuthenticated = ({
fontSize: '12px', fontSize: '12px',
}} }}
> >
Build version: {manifestData?.version} {t('auth:build_version', { postProcess: 'capitalize' })}:
{manifestData?.version}
</Typography> </Typography>
</Box> </Box>
</> </>
@ -788,7 +791,7 @@ export const NotAuthenticated = ({
}} }}
variant="contained" variant="contained"
> >
Choose {t('core:choose', { postProcess: 'capitalize' })}
</Button> </Button>
</Box> </Box>
</Box> </Box>