feat: add language selector settings

This commit is contained in:
Nicolas Meienberger 2023-05-15 21:37:47 +02:00 committed by Nicolas Meienberger
parent e363b3f8db
commit a767c44651
9 changed files with 78 additions and 39 deletions

View file

@ -0,0 +1,27 @@
import React from 'react';
import { useLocale } from '@/client/hooks/useLocale';
import { LOCALE_OPTIONS, Locale } from '@/shared/internationalization/locales';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../ui/Select';
export const LanguageSelector = () => {
const { locale, changeLocale } = useLocale();
const onChange = (value: Locale) => {
changeLocale(value);
};
return (
<Select value={locale} defaultValue={locale} onValueChange={onChange}>
<SelectTrigger className="mb-3" label="">
<SelectValue placeholder="test" />
</SelectTrigger>
<SelectContent>
{LOCALE_OPTIONS.map((option) => (
<SelectItem key={option.value} value={option.value}>
{option.label}
</SelectItem>
))}
</SelectContent>
</Select>
);
};

View file

@ -0,0 +1 @@
export { LanguageSelector } from './LanguageSelector';

View file

@ -66,7 +66,7 @@ describe('test: useLocale()', () => {
// assert
await waitFor(() => {
expect(result.current.locale).toEqual('en');
expect(result.current.locale).toEqual('en-US');
});
});
});

View file

@ -11,10 +11,12 @@ export const useLocale = () => {
const browserLocale = typeof window !== 'undefined' ? window.navigator.language : undefined;
const locale = me.data?.locale || cookies.locale || browserLocale || 'en';
const ctx = trpc.useContext();
const changeLocale = async (l: Locale) => {
if (me.data) {
await changeUserLocale.mutateAsync({ locale: l });
await ctx.invalidate();
}
setCookie(null, 'locale', l, {

View file

@ -244,7 +244,8 @@
"apps-repo-hint": "URL to the apps repository.",
"storage-path": "Storage path",
"storage-path-hint": "Path to the storage directory. Keep empty for default (runtipi/app-data). Make sure it is an absolute path and that it exists",
"submit": "Save"
"submit": "Save",
"user-settings-title": "User settings"
},
"security": {
"tab-title": "Security",

View file

@ -1,5 +1,6 @@
import Image from 'next/image';
import React from 'react';
import { LanguageSelector } from '@/components/LanguageSelector';
import { getUrl } from '../../../../core/helpers/url-helpers';
interface IProps {
@ -8,6 +9,9 @@ interface IProps {
export const AuthFormLayout: React.FC<IProps> = ({ children }) => (
<div className="page page-center">
<div className="position-absolute top-0 mt-3 end-0 me-1 pb-4">
<LanguageSelector />
</div>
<div className="container container-tight py-4">
<div className="text-center mb-4">
<Image

View file

@ -1,5 +1,7 @@
import { LanguageSelector } from '@/components/LanguageSelector';
import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input';
import { IconAdjustmentsAlt, IconUser } from '@tabler/icons-react';
import { useTranslations } from 'next-intl';
import React, { useEffect } from 'react';
import { useForm } from 'react-hook-form';
@ -85,31 +87,41 @@ export const SettingsForm = (props: IProps) => {
};
return (
<form className="flex flex-col" onSubmit={handleSubmit(validate)}>
<h2 className="text-2xl font-bold">{t('title')}</h2>
<p className="mb-4">{t('subtitle')}</p>
<div className="mb-3">
<Input {...register('domain')} label={t('domain-name')} error={errors.domain?.message} placeholder="tipi.localhost" />
<span className="text-muted">{t('domain-name-hint')}</span>
<>
<div className="d-flex">
<IconUser className="me-2" />
<h2 className="text-2xl font-bold">{t('user-settings-title')}</h2>
</div>
<div className="mb-3">
<Input {...register('dnsIp')} label={t('dns-ip')} error={errors.dnsIp?.message} placeholder="9.9.9.9" />
</div>
<div className="mb-3">
<Input {...register('internalIp')} label={t('internal-ip')} error={errors.internalIp?.message} placeholder="192.168.1.100" />
<span className="text-muted">{t('internal-ip-hint')}</span>
</div>
<div className="mb-3">
<Input {...register('appsRepoUrl')} label={t('apps-repo')} error={errors.appsRepoUrl?.message} placeholder="https://github.com/meienberger/runtipi-appstore" />
<span className="text-muted">{t('apps-repo-hint')}</span>
</div>
<div className="mb-3">
<Input {...register('storagePath')} label={t('storage-path')} error={errors.storagePath?.message} placeholder="Storage path" />
<span className="text-muted">{t('storage-path-hint')}</span>
</div>
<Button loading={loading} type="submit" className="btn-success">
{t('submit')}
</Button>
</form>
<LanguageSelector />
<form className="flex flex-col mt-2" onSubmit={handleSubmit(validate)}>
<div className="d-flex">
<IconAdjustmentsAlt className="me-2" />
<h2 className="text-2xl font-bold">{t('title')}</h2>
</div>
<p className="mb-4">{t('subtitle')}</p>
<div className="mb-3">
<Input {...register('domain')} label={t('domain-name')} error={errors.domain?.message} placeholder="tipi.localhost" />
<span className="text-muted">{t('domain-name-hint')}</span>
</div>
<div className="mb-3">
<Input {...register('dnsIp')} label={t('dns-ip')} error={errors.dnsIp?.message} placeholder="9.9.9.9" />
</div>
<div className="mb-3">
<Input {...register('internalIp')} label={t('internal-ip')} error={errors.internalIp?.message} placeholder="192.168.1.100" />
<span className="text-muted">{t('internal-ip-hint')}</span>
</div>
<div className="mb-3">
<Input {...register('appsRepoUrl')} label={t('apps-repo')} error={errors.appsRepoUrl?.message} placeholder="https://github.com/meienberger/runtipi-appstore" />
<span className="text-muted">{t('apps-repo-hint')}</span>
</div>
<div className="mb-3">
<Input {...register('storagePath')} label={t('storage-path')} error={errors.storagePath?.message} placeholder="Storage path" />
<span className="text-muted">{t('storage-path-hint')}</span>
</div>
<Button loading={loading} type="submit" className="btn-success">
{t('submit')}
</Button>
</form>
</>
);
};

View file

@ -29,14 +29,6 @@ export const getMessagesPageProps: GetServerSideProps = async (ctx) => {
const locale = getLocaleFromString(sessionLocale || cookieLocale || browserLocale || 'en');
const englishMessages = (await import(`../messages/en.json`)).default;
if (locale === 'en') {
return {
props: {
messages: englishMessages,
},
};
}
const messages = (await import(`../messages/${locale}.json`)).default;
const mergedMessages = merge(englishMessages, messages);

View file

@ -1,5 +1,5 @@
export const APP_LOCALES = {
en: 'English',
'en-US': 'English',
'fr-FR': 'Français',
'ja-JP': '日本語',
'ro-RO': 'Română',
@ -10,7 +10,7 @@ export const APP_LOCALES = {
const FALLBACK_LOCALES = [
{ from: 'fr', to: 'fr-FR' },
{ from: 'en', to: 'en' },
{ from: 'en', to: 'en-US' },
{ from: 'ja', to: 'ja-JP' },
{ from: 'ro', to: 'ro-RO' },
{ from: 'ru', to: 'ru-RU' },
@ -24,7 +24,7 @@ export const LOCALE_OPTIONS = Object.entries(APP_LOCALES).map(([value, label]) =
export const getLocaleFromString = (locale?: string): Locale => {
if (!locale) {
return 'en';
return 'en-US';
}
if (Locales.includes(locale)) {
@ -36,5 +36,5 @@ export const getLocaleFromString = (locale?: string): Locale => {
return fallback.to as Locale;
}
return 'en';
return 'en-US';
};