feat: auto detect language (#947)

* added auto-detect language

* fix(TranslationSelect) now saving the selected language between sessions

* fix(LangSelector.spec)

* fix(conflict)

* fix(Swedish) sv-SE
This commit is contained in:
Marco Beretta 2023-09-18 21:40:20 +02:00 committed by GitHub
parent 2419af8748
commit b48c618f32
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 329 additions and 328 deletions

View file

@ -36,7 +36,7 @@ export default function Settings({ open, onOpenChange }: TDialogProps) {
>
<Tabs.Trigger
className={cn(
'group flex items-center justify-start gap-2 rounded-md px-2 py-1.5 text-sm radix-state-active:bg-gray-800 radix-state-active:text-white',
'group flex items-center justify-start gap-2 rounded-md px-2 py-1.5 text-sm text-gray-500 radix-state-active:bg-gray-800 radix-state-active:text-white',
isSmallScreen
? 'flex-1 items-center justify-center text-sm dark:text-gray-500 dark:radix-state-active:text-white'
: '',
@ -48,7 +48,7 @@ export default function Settings({ open, onOpenChange }: TDialogProps) {
</Tabs.Trigger>
<Tabs.Trigger
className={cn(
'group flex items-center justify-start gap-2 rounded-md px-2 py-1.5 text-sm radix-state-active:bg-gray-800 radix-state-active:text-white',
'group flex items-center justify-start gap-2 rounded-md px-2 py-1.5 text-sm text-gray-500 radix-state-active:bg-gray-800 radix-state-active:text-white',
isSmallScreen
? 'flex-1 items-center justify-center text-sm dark:text-gray-500 dark:radix-state-active:text-white'
: '',

View file

@ -12,6 +12,7 @@ import {
import type { TDangerButtonProps } from '~/common';
import DangerButton from './DangerButton';
import store from '~/store';
import useLocalStorage from '~/hooks/useLocalStorage';
export const ThemeSelector = ({
theme,
@ -82,17 +83,18 @@ export const LangSelector = ({
onChange={(e) => onChange(e.target.value)}
value={langcode}
>
<option value="en">{localize('com_nav_lang_english')}</option>
<option value="cn">{localize('com_nav_lang_chinese')}</option>
<option value="de">{localize('com_nav_lang_german')}</option>
<option value="es">{localize('com_nav_lang_spanish')}</option>
<option value="fr">{localize('com_nav_lang_french')}</option>
<option value="it">{localize('com_nav_lang_italian')}</option>
<option value="pl">{localize('com_nav_lang_polish')}</option>
<option value="br">{localize('com_nav_lang_brazilian_portuguese')}</option>
<option value="ru">{localize('com_nav_lang_russian')}</option>
<option value="jp">{localize('com_nav_lang_japanese')}</option>
<option value="sv">{localize('com_nav_lang_swedish')}</option>
<option value="auto">{localize('com_nav_lang_auto')}</option>
<option value="en-US">{localize('com_nav_lang_english')}</option>
<option value="zh-CN">{localize('com_nav_lang_chinese')}</option>
<option value="de-DE">{localize('com_nav_lang_german')}</option>
<option value="es-ES">{localize('com_nav_lang_spanish')}</option>
<option value="fr-FR">{localize('com_nav_lang_french')}</option>
<option value="it-IT">{localize('com_nav_lang_italian')}</option>
<option value="pl-PL">{localize('com_nav_lang_polish')}</option>
<option value="pt-BR">{localize('com_nav_lang_brazilian_portuguese')}</option>
<option value="ru-RU">{localize('com_nav_lang_russian')}</option>
<option value="ja-JP">{localize('com_nav_lang_japanese')}</option>
<option value="sv-SE">{localize('com_nav_lang_swedish')}</option>
</select>
</div>
);
@ -103,6 +105,7 @@ function General() {
const clearConvosMutation = useClearConversationsMutation();
const [confirmClear, setConfirmClear] = useState(false);
const [langcode, setLangcode] = useRecoilState(store.lang);
const [selectedLang, setSelectedLang] = useLocalStorage('selectedLang', langcode);
const { newConversation } = useConversation();
const { refreshConversations } = useConversations();
@ -135,9 +138,17 @@ function General() {
const changeLang = useCallback(
(value: string) => {
setLangcode(value);
setSelectedLang(value);
if (value === 'auto') {
const userLang = navigator.language || navigator.languages[0];
setLangcode(userLang);
localStorage.setItem('lang', userLang);
} else {
setLangcode(value);
localStorage.setItem('lang', value);
}
},
[setLangcode],
[setLangcode, setSelectedLang],
);
return (
@ -152,7 +163,7 @@ function General() {
<ThemeSelector theme={theme} onChange={changeTheme} />
</div>
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
<LangSelector langcode={langcode} onChange={changeLang} />
<LangSelector langcode={selectedLang} onChange={changeLang} />
</div>
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
<ClearChatsButton

View file

@ -15,7 +15,7 @@ describe('LangSelector', () => {
it('renders correctly', () => {
const { getByText, getByDisplayValue } = render(
<RecoilRoot>
<LangSelector langcode="en" onChange={mockOnChange} />
<LangSelector langcode="en-US" onChange={mockOnChange} />
</RecoilRoot>,
);
@ -26,12 +26,12 @@ describe('LangSelector', () => {
it('calls onChange when the select value changes', () => {
const { getByDisplayValue } = render(
<RecoilRoot>
<LangSelector langcode="en" onChange={mockOnChange} />
<LangSelector langcode="en-US" onChange={mockOnChange} />
</RecoilRoot>,
);
fireEvent.change(getByDisplayValue('English'), { target: { value: 'it' } });
fireEvent.change(getByDisplayValue('English'), { target: { value: 'it-IT' } });
expect(mockOnChange).toHaveBeenCalledWith('it');
expect(mockOnChange).toHaveBeenCalledWith('it-IT');
});
});