Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/mobile/app/(main)/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export default function MainLayout(): ReactElement {
}}>
<Stack.Screen name={navigationConfig.main.chat.index} />
<Stack.Screen name={navigationConfig.main.folder.index} />
<Stack.Screen name={navigationConfig.main.settings} />
</Stack>
);
}
6 changes: 5 additions & 1 deletion apps/mobile/app/(main)/chat/list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ export default function ChatListScreen(): ReactElement {
const handleArchivedChatsPress = (): void =>
navigateOnce(`${navigationConfig.main.chat.index}/${navigationConfig.main.chat.archivedChats}`);

const handleSettingsPress = (): void => navigateOnce(navigationConfig.main.settings);

const handleFolderPress = (id: string, title: string): void =>
router.navigate(navigationConfig.main.folder.view({ id, title }));

Expand All @@ -49,7 +51,9 @@ export default function ChatListScreen(): ReactElement {
<AppHeader
className='mt-0'
title={translate('TEXT_CHATS')}
accessoryLeft={<ProfileMenuSheet onArchivedChatsPress={handleArchivedChatsPress} />}
accessoryLeft={
<ProfileMenuSheet onArchivedChatsPress={handleArchivedChatsPress} onSettingsPress={handleSettingsPress} />
}
accessoryRight={
<View className='flex-row gap-12'>
{isFeatureEnabled(FeatureID.CHAT_FOLDERS) && (
Expand Down
43 changes: 43 additions & 0 deletions apps/mobile/app/(main)/settings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Host, Picker } from '@expo/ui';
import { useSelector } from '@legendapp/state/react';
import { useColorScheme } from '@open-webui-react-native/mobile/shared/ui/styles';
import { AppHeader, AppScreen, AppText, View } from '@open-webui-react-native/mobile/shared/ui/ui-kit';
import { appState$ } from '@open-webui-react-native/shared/data-access/app-state';
import { availableLanguages, LanguageCode } from '@open-webui-react-native/shared/utils/config';
import { useTranslation } from '@ronas-it/react-native-common-modules/i18n';
import { useRouter } from 'expo-router';
import { ReactElement } from 'react';

// TODO: Temporary solution, update when design is ready
export default function SettingsScreen(): ReactElement {
const router = useRouter();
const translate = useTranslation('APP.SETTINGS_SCREEN');
const { colorScheme } = useColorScheme();
const locale = useSelector(appState$.locale);

const handleLanguageChange = (value: LanguageCode): void => {
appState$.setLocale(value);
};

return (
<AppScreen
header={<AppHeader
title={translate('TEXT_SETTINGS')}
onGoBack={router.back}
titleClassName='max-w-[65%]' />}>
<View className='flex-row items-center justify-between py-12'>
<AppText className='text-h4-sm sm:text-h4'>{translate('TEXT_LANGUAGE')}</AppText>
<Host matchContents colorScheme={colorScheme}>
<Picker selectedValue={locale} onValueChange={handleLanguageChange}>
{availableLanguages.map(({ code, label }) => (
<Picker.Item
key={code}
label={label}
value={code} />
))}
</Picker>
</Host>
</View>
</AppScreen>
);
}
73 changes: 68 additions & 5 deletions apps/mobile/app/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,16 @@ import { queryClient } from '@open-webui-react-native/shared/data-access/query-c
import { useSocket } from '@open-webui-react-native/shared/data-access/websocket';
import { useNetworkConnection } from '@open-webui-react-native/shared/features/network';
import { analyticsService } from '@open-webui-react-native/shared/utils/analytics-service';
import { constants } from '@open-webui-react-native/shared/utils/config';
import { constants, LanguageCode } from '@open-webui-react-native/shared/utils/config';
import { setupReactotron } from '@open-webui-react-native/shared/utils/reactotron';
import { setLanguage } from '@ronas-it/react-native-common-modules/i18n';
import { i18n, setLanguage } from '@ronas-it/react-native-common-modules/i18n';
import * as Sentry from '@sentry/react-native';
import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client';
import Constants from 'expo-constants';
import { useFonts } from 'expo-font';
import { SplashScreen, Stack, useNavigationContainerRef } from 'expo-router';
import { useMakePlural } from 'i18n-js';
import { de, en, es, fr, ja, pt, ru, zh } from 'make-plural';
import { ReactElement, useEffect } from 'react';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { KeyboardProvider } from 'react-native-keyboard-controller';
Expand All @@ -44,16 +46,76 @@ Sentry.init({
});

const translations = {
[constants.defaultLocale]: {
[LanguageCode.ENGLISH]: {
...require('i18n/mobile/app/en.json'),
...require('i18n/mobile/shared/en.json'),
...require('i18n/mobile/auth/en.json'),
...require('i18n/mobile/chat/en.json'),
...require('i18n/mobile/profile/en.json'),
...require('i18n/mobile/folder/en.json'),
},
[LanguageCode.RUSSIAN]: {
...require('i18n/mobile/app/ru.json'),
...require('i18n/mobile/shared/ru.json'),
...require('i18n/mobile/auth/ru.json'),
...require('i18n/mobile/chat/ru.json'),
...require('i18n/mobile/profile/ru.json'),
...require('i18n/mobile/folder/ru.json'),
},
[LanguageCode.SPANISH]: {
...require('i18n/mobile/app/es.json'),
...require('i18n/mobile/shared/es.json'),
...require('i18n/mobile/auth/es.json'),
...require('i18n/mobile/chat/es.json'),
...require('i18n/mobile/profile/es.json'),
...require('i18n/mobile/folder/es.json'),
},
[LanguageCode.PORTUGUESE]: {
...require('i18n/mobile/app/pt.json'),
...require('i18n/mobile/shared/pt.json'),
...require('i18n/mobile/auth/pt.json'),
...require('i18n/mobile/chat/pt.json'),
...require('i18n/mobile/profile/pt.json'),
...require('i18n/mobile/folder/pt.json'),
},
[LanguageCode.FRENCH]: {
...require('i18n/mobile/app/fr.json'),
...require('i18n/mobile/shared/fr.json'),
...require('i18n/mobile/auth/fr.json'),
...require('i18n/mobile/chat/fr.json'),
...require('i18n/mobile/profile/fr.json'),
...require('i18n/mobile/folder/fr.json'),
},
[LanguageCode.GERMAN]: {
...require('i18n/mobile/app/de.json'),
...require('i18n/mobile/shared/de.json'),
...require('i18n/mobile/auth/de.json'),
...require('i18n/mobile/chat/de.json'),
...require('i18n/mobile/profile/de.json'),
...require('i18n/mobile/folder/de.json'),
},
[LanguageCode.CHINESE]: {
...require('i18n/mobile/app/zh.json'),
...require('i18n/mobile/shared/zh.json'),
...require('i18n/mobile/auth/zh.json'),
...require('i18n/mobile/chat/zh.json'),
...require('i18n/mobile/profile/zh.json'),
...require('i18n/mobile/folder/zh.json'),
},
[LanguageCode.JAPANESE]: {
...require('i18n/mobile/app/ja.json'),
...require('i18n/mobile/shared/ja.json'),
...require('i18n/mobile/auth/ja.json'),
...require('i18n/mobile/chat/ja.json'),
...require('i18n/mobile/profile/ja.json'),
...require('i18n/mobile/folder/ja.json'),
},
};

Object.entries({ en, ru, es, pt, fr, de, zh, ja }).forEach(([locale, pluralizer]) => {
i18n.pluralization.register(locale, useMakePlural({ pluralizer }));
});

const useLanguage = setLanguage(translations, constants.defaultLocale);

setupReactotron('open-web-ui');
Expand Down Expand Up @@ -101,7 +163,8 @@ function App(): ReactElement | null {
}

function RootLayout(): ReactElement | null {
useLanguage(constants.defaultLocale);
const locale = useSelector(appState$.locale);
useLanguage(locale);
const [isFontsLoaded] = useFonts(fonts);
const navigationContainerRef = useNavigationContainerRef();

Expand Down Expand Up @@ -136,7 +199,7 @@ function RootLayout(): ReactElement | null {
}}>
<ToastProvider>
<BottomSheetModalProvider>
<App />
<App key={locale} />
</BottomSheetModalProvider>
</ToastProvider>
</PersistQueryClientProvider>
Expand Down
3 changes: 2 additions & 1 deletion apps/mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"@amplitude/analytics-react-native": "^1.6.8",
"@expo/metro-config": "~57.0.0",
"@expo/metro-runtime": "~57.0.7",
"@expo/ui": "~57.0.7",
"@expo/ui": "~57.0.10",
"@gorhom/bottom-sheet": "^5.2.8",
"@hookform/resolvers": "^5.1.1",
"@legendapp/state": "2.1.15",
Expand Down Expand Up @@ -80,6 +80,7 @@
"immer": "^10.1.1",
"lodash-es": "^4.17.21",
"luxon": "^3.6.1",
"make-plural": "^7.5.0",
"markdown-it-math": "^5.2.1",
"nativewind": "^4.1.23",
"patch-package": "^8.0.0",
Expand Down
2 changes: 1 addition & 1 deletion apps/mobile/tsconfig.app.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"outDir": "../../dist/out-tsc",
"types": ["node"]
},
"files": ["../../node_modules/@nx/expo/typings/svg.d.ts"],
"files": ["../../node_modules/@nx/expo/typings/svg.d.ts", "./nativewind-env.d.ts"],
"exclude": ["jest.config.ts", "**/*.spec.ts", "**/*.spec.tsx", "test-setup.ts"],
"include": ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"]
}
11 changes: 11 additions & 0 deletions i18n/mobile/app/de.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"APP": {
"CHAT_LIST_SCREEN": {
"TEXT_CHATS": "Chats"
},
"SETTINGS_SCREEN": {
"TEXT_SETTINGS": "Einstellungen",
"TEXT_LANGUAGE": "Sprache"
}
}
}
4 changes: 4 additions & 0 deletions i18n/mobile/app/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
"APP": {
"CHAT_LIST_SCREEN": {
"TEXT_CHATS": "Chats"
},
"SETTINGS_SCREEN": {
"TEXT_SETTINGS": "Settings",
"TEXT_LANGUAGE": "Language"
}
}
}
11 changes: 11 additions & 0 deletions i18n/mobile/app/es.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"APP": {
"CHAT_LIST_SCREEN": {
"TEXT_CHATS": "Chats"
},
"SETTINGS_SCREEN": {
"TEXT_SETTINGS": "Ajustes",
"TEXT_LANGUAGE": "Idioma"
}
}
}
11 changes: 11 additions & 0 deletions i18n/mobile/app/fr.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"APP": {
"CHAT_LIST_SCREEN": {
"TEXT_CHATS": "Discussions"
},
"SETTINGS_SCREEN": {
"TEXT_SETTINGS": "Paramètres",
"TEXT_LANGUAGE": "Langue"
}
}
}
11 changes: 11 additions & 0 deletions i18n/mobile/app/ja.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"APP": {
"CHAT_LIST_SCREEN": {
"TEXT_CHATS": "チャット"
},
"SETTINGS_SCREEN": {
"TEXT_SETTINGS": "設定",
"TEXT_LANGUAGE": "言語"
}
}
}
11 changes: 11 additions & 0 deletions i18n/mobile/app/pt.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"APP": {
"CHAT_LIST_SCREEN": {
"TEXT_CHATS": "Chats"
},
"SETTINGS_SCREEN": {
"TEXT_SETTINGS": "Configurações",
"TEXT_LANGUAGE": "Idioma"
}
}
}
11 changes: 11 additions & 0 deletions i18n/mobile/app/ru.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"APP": {
"CHAT_LIST_SCREEN": {
"TEXT_CHATS": "Чаты"
},
"SETTINGS_SCREEN": {
"TEXT_SETTINGS": "Настройки",
"TEXT_LANGUAGE": "Язык"
}
}
}
11 changes: 11 additions & 0 deletions i18n/mobile/app/zh.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"APP": {
"CHAT_LIST_SCREEN": {
"TEXT_CHATS": "聊天"
},
"SETTINGS_SCREEN": {
"TEXT_SETTINGS": "设置",
"TEXT_LANGUAGE": "语言"
}
}
}
25 changes: 25 additions & 0 deletions i18n/mobile/auth/de.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"AUTH": {
"SIGN_IN": {
"TEXT_TITLE_EXTERNAL": "Anmelden",
"TEXT_YOU_LOGGED_IN": "Sie sind jetzt angemeldet.",
"EMAIL_FORM": {
"TEXT_EMAIL_ADDRESS": "E-Mail-Adresse",
"TEXT_PASSWORD": "Passwort",
"TEXT_API_URL": "API-URL",
"TEXT_ENTER_YOUR_PASSWORD": "Passwort eingeben",
"BUTTON_SIGN_IN": "Anmelden"
},
"GOOGLE_FORM": {
"BUTTON_CONTINUE_WITH_GOOGLE": "Mit Google fortfahren"
},
"OIDC_FORM": {
"BUTTON_CONTINUE_WITH": "Mit {{provider}} fortfahren"
},
"OAUTH_WEB_VIEW_MODAL": {
"BUTTON_CLOSE": "Schließen",
"TEXT_THIS_SIGN_IN_METHOD_IS_UNAVAILABLE": "Diese Anmeldemethode ist nicht verfügbar. Bitte wählen Sie eine andere Option."
}
}
}
}
25 changes: 25 additions & 0 deletions i18n/mobile/auth/es.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"AUTH": {
"SIGN_IN": {
"TEXT_TITLE_EXTERNAL": "Iniciar sesión",
"TEXT_YOU_LOGGED_IN": "Has iniciado sesión.",
"EMAIL_FORM": {
"TEXT_EMAIL_ADDRESS": "Correo electrónico",
"TEXT_PASSWORD": "Contraseña",
"TEXT_API_URL": "URL de la API",
"TEXT_ENTER_YOUR_PASSWORD": "Introduce tu contraseña",
"BUTTON_SIGN_IN": "Iniciar sesión"
},
"GOOGLE_FORM": {
"BUTTON_CONTINUE_WITH_GOOGLE": "Continuar con Google"
},
"OIDC_FORM": {
"BUTTON_CONTINUE_WITH": "Continuar con {{provider}}"
},
"OAUTH_WEB_VIEW_MODAL": {
"BUTTON_CLOSE": "Cerrar",
"TEXT_THIS_SIGN_IN_METHOD_IS_UNAVAILABLE": "Este método de inicio de sesión no está disponible. Usa otra opción para continuar."
}
}
}
}
25 changes: 25 additions & 0 deletions i18n/mobile/auth/fr.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"AUTH": {
"SIGN_IN": {
"TEXT_TITLE_EXTERNAL": "Connexion",
"TEXT_YOU_LOGGED_IN": "Vous êtes maintenant connecté.",
"EMAIL_FORM": {
"TEXT_EMAIL_ADDRESS": "Adresse e-mail",
"TEXT_PASSWORD": "Mot de passe",
"TEXT_API_URL": "URL de l’API",
"TEXT_ENTER_YOUR_PASSWORD": "Entrez votre mot de passe",
"BUTTON_SIGN_IN": "Se connecter"
},
"GOOGLE_FORM": {
"BUTTON_CONTINUE_WITH_GOOGLE": "Continuer avec Google"
},
"OIDC_FORM": {
"BUTTON_CONTINUE_WITH": "Continuer avec {{provider}}"
},
"OAUTH_WEB_VIEW_MODAL": {
"BUTTON_CLOSE": "Fermer",
"TEXT_THIS_SIGN_IN_METHOD_IS_UNAVAILABLE": "Cette méthode de connexion est indisponible. Veuillez utiliser une autre option."
}
}
}
}
Loading
Loading