Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Implemented i18n translations #108

Merged
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
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,8 @@ src/api/generated-api.*
.env
.env.production

devices.json
devices.json

# VSCode
/.vscode/*
!/.vscode/extensions.json
5 changes: 5 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"recommendations": [
"lokalise.i18n-ally"
]
}
9 changes: 6 additions & 3 deletions app/(auth)/_layout.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import { useTranslation } from 'react-i18next';

import { Stack } from '@/layout/Stack';

const AuthStack = () => {
const { t } = useTranslation();
return (
<Stack
screens={[
{
route: 'onboarding',
title: 'Onboarding',
title: t('layouts:auth.onboarding'),
options: { headerShown: false },
},
{ route: 'login', title: 'Login' },
{ route: 'register', title: 'Create Account' },
{ route: 'login', title: t('layouts:auth.login') },
{ route: 'register', title: t('layouts:auth.register') },
]}
/>
);
Expand Down
26 changes: 17 additions & 9 deletions app/(auth)/login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useState } from 'react';

import { Formiz, useForm, useFormContext, useFormFields } from '@formiz/core';
import { isEmail } from '@formiz/validations';
import { useTranslation } from 'react-i18next';
import {
Box,
Button,
Expand All @@ -24,17 +25,18 @@ import { useToast } from '@/modules/toast/useToast';
import { useDarkMode } from '@/theme/useDarkMode';

const CardInfoAuthStep = () => {
const { t } = useTranslation();
const loginForm = useFormContext();
const { colorModeValue } = useDarkMode();
return (
<CardStatus type="info" title="Demo mode" mt="md">
<CardStatus type="info" title={t('login:card.title')} mt="md">
<Box flexDirection="row" alignItems="center" flexWrap="wrap">
<Text
fontSize="lg"
color={colorModeValue('gray.800', 'gray.50')}
my="sm"
>
Enjoy the features! You can sign in with{' '}
{t('login:card.description')}{' '}
</Text>
<TouchableOpacity
onPress={() => loginForm.setValues({ email: '[email protected]' })}
Expand All @@ -54,6 +56,7 @@ const CardInfoAuthStep = () => {
};

const Login = () => {
const { t } = useTranslation();
const loginForm = useForm<{
email: string;
code: string;
Expand Down Expand Up @@ -87,7 +90,7 @@ const Login = () => {
validateEmailCodeModal.onOpen();
},
onError: (err) => {
showError('Failed to log in. Please try again');
showError(t('login:feedbacks.error'));
console.error('Authentication error:', err);
},
});
Expand All @@ -96,14 +99,14 @@ const Login = () => {
useAuthLoginValidate(emailToken as string, {
onSuccess: () => {
validateEmailCodeModal.onClose();
showSuccess('Successfully logged in');
showSuccess(t('login:validation.success'));
},
onError: () => {
emailValidationCodeForm.setValues({
code: null,
});
emailValidationCodeForm.setErrors({
code: 'Code is incorrect, please try again',
code: t('login:validation.error'),
});
},
});
Expand All @@ -114,9 +117,14 @@ const Login = () => {
<Content>
<FieldInput
name="email"
label="Mail address"
required="Mail is required"
validations={[{ handler: isEmail(), message: 'Mail is invalid' }]}
label={t('login:input.label')}
required={t('login:input.required')}
validations={[
{
handler: isEmail(),
message: t('login:input.validations.email'),
},
]}
componentProps={{
autoCapitalize: 'none',
keyboardType: 'email-address',
Expand All @@ -133,7 +141,7 @@ const Login = () => {
colorScheme="brand"
full
>
Sign in
{t('login:actions.login')}
</Button>
</Footer>
</Formiz>
Expand Down
12 changes: 7 additions & 5 deletions app/(auth)/onboarding.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { useRouter } from 'expo-router';
import { useTranslation } from 'react-i18next';
import { Image } from 'react-native';
import { Button, Icon, Stack, Text } from 'react-native-ficus-ui';

import { useDarkMode } from '@/theme/useDarkMode';

const Onboarding = () => {
const router = useRouter();
const { t } = useTranslation();
const { colorModeValue, toggleColorMode, colorMode } = useDarkMode();

// eslint-disable-next-line @typescript-eslint/no-var-requires
Expand All @@ -27,14 +29,14 @@ const Onboarding = () => {
width: '100%',
height: 80,
}}
accessibilityLabel="Start UI Native Logo"
accessibilityLabel={t('onboarding:logo')}
/>
<Text
fontSize="lg"
textAlign="center"
color={colorModeValue('gray.900', 'gray.50')}
>
An opinionated UI starter with Expo, Ficus UI, React Query & Formiz
{t('onboarding:description')}
</Text>
</Stack>
<Stack spacing="md" alignItems="center">
Expand All @@ -44,22 +46,22 @@ const Onboarding = () => {
onPress={() => router.push('/register')}
colorScheme="brand"
>
Sign up with mail
{t('onboarding:actions.register')}
</Button>
<Stack direction="row" alignItems="center">
<Text
onPress={handleOpenLogin}
color={colorModeValue('gray.900', 'gray.50')}
>
Already an account?
{t('onboarding:actions.alreadyHaveAnAccount')}
</Text>
<Button onPress={handleOpenLogin} colorScheme="transparent">
<Text
fontWeight="500"
textDecorLine="underline"
color={colorModeValue('gray.900', 'gray.50')}
>
Sign in
{t('onboarding:actions.login')}
</Text>
</Button>
</Stack>
Expand Down
30 changes: 16 additions & 14 deletions app/(auth)/register.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useRef, useState } from 'react';
import { Formiz, useForm, useFormFields } from '@formiz/core';
import { isEmail } from '@formiz/validations';
import { useRouter } from 'expo-router';
import { useTranslation } from 'react-i18next';
import { TextInput } from 'react-native';
import { Box, Button, Stack, Text, useDisclosure } from 'react-native-ficus-ui';

Expand All @@ -22,17 +23,17 @@ import { focus } from '@/utils/formUtils';

const CardWarningRegister = () => {
const router = useRouter();
const { t } = useTranslation();
const { colorModeValue } = useDarkMode();
return (
<CardStatus type="warning" title="Demo mode" mt="md">
<CardStatus type="warning" title={t('register:card.title')} mt="md">
<Box flexDirection="row" flexWrap="wrap">
<Text
fontSize="lg"
color={colorModeValue('gray.800', 'gray.50')}
my="sm"
>
This is a read-only demo, but you can Sign in to test some of the
features. Just remember, no changes can be made. Enjoy the features!
{t('register:card.description')}
</Text>
<Button
onPress={() => router.push('/login')}
Expand All @@ -41,7 +42,7 @@ const CardWarningRegister = () => {
bg={colorModeValue(undefined, 'gray.800')}
variant={colorModeValue('outline', 'solid')}
>
Sign in
{t('register:card.actions.login')}
</Button>
</Box>
</CardStatus>
Expand All @@ -50,6 +51,7 @@ const CardWarningRegister = () => {

const Register = () => {
const { showError, showSuccess } = useToast();
const { t } = useTranslation();
const nameRef = useRef<TextInput>(null);
const validateEmailCodeModal = useDisclosure();
const [emailToken, setEmailToken] = useState<string | null>(null);
Expand Down Expand Up @@ -84,8 +86,8 @@ const Register = () => {
onError: (err) => {
showError(
err.response?.data?.message?.startsWith('[DEMO]')
? 'This is a read-only demo, this action is disabled.'
: 'An error occured during your registration, please try again'
? t('register:feedbacks.createAccount.error.demo')
: t('register:feedbacks.createAccount.error.default')
);
},
});
Expand All @@ -94,14 +96,14 @@ const Register = () => {
useAuthRegisterValidate(emailToken as string, {
onSuccess: () => {
validateEmailCodeModal.onClose();
showSuccess('Successfully logged in');
showSuccess(t('register:feedbacks.accountValidate.success'));
},
onError: () => {
emailValidationCodeForm.setValues({
code: null,
});
emailValidationCodeForm.setErrors({
code: 'Code is incorrect, please try again',
code: t('register:feedbacks.accountValidate.error'),
});
},
});
Expand All @@ -113,12 +115,12 @@ const Register = () => {
<Stack spacing="md">
<FieldInput
name="email"
label="Mail address"
required="Mail is required"
label={t('register:inputs.email.label')}
required={t('register:inputs.email.required')}
validations={[
{
handler: isEmail(),
message: 'Mail is invalid',
message: t('register:inputs.email.validations.email'),
},
]}
componentProps={{
Expand All @@ -134,8 +136,8 @@ const Register = () => {
<FieldInput
ref={nameRef}
name="name"
label="Name"
required="Name is required"
label={t('register:inputs.name.label')}
required={t('register:inputs.name.required')}
componentProps={{
autoCapitalize: 'none',
returnKeyType: 'next',
Expand All @@ -154,7 +156,7 @@ const Register = () => {
colorScheme="brand"
full
>
Sign up
{t('register:actions.register')}
</Button>
</Footer>
</Formiz>
Expand Down
9 changes: 6 additions & 3 deletions app/(tabs)/_layout.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
import { useTranslation } from 'react-i18next';

import { Tabs } from '@/layout/Tabs';

const HomeTabs = () => {
const { t } = useTranslation();
return (
<Tabs
screens={[
{
route: 'home',
title: 'Home',
title: t('layouts:tabs.home'),
icon: 'home',
options: { headerShown: false },
},
{
route: 'repositories',
title: 'Repositories',
title: t('layouts:tabs.repositories'),
icon: 'folder',
options: { headerShown: false },
},
{
route: 'account',
title: 'Account',
title: t('layouts:tabs.account'),
icon: 'user',
options: { headerShown: false },
},
Expand Down
7 changes: 6 additions & 1 deletion app/(tabs)/account/_layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { useTranslation } from 'react-i18next';

import { Stack } from '@/layout/Stack';

const AccountStack = () => {
return <Stack screens={[{ route: 'index', title: 'Account' }]} />;
const { t } = useTranslation();
return (
<Stack screens={[{ route: 'index', title: t('layouts:tabs.account') }]} />
);
};

export default AccountStack;
Loading