Add i18next integration

feature/app-setup
vinay kumar 2025-06-28 09:57:23 +05:30
parent 363b88fd92
commit 48877cbf0f
6 changed files with 86 additions and 24 deletions

View File

@ -1,11 +1,11 @@
import React from 'react'; import React from "react";
import FontAwesome from '@expo/vector-icons/FontAwesome'; import FontAwesome from "@expo/vector-icons/FontAwesome";
import { Link, Tabs } from 'expo-router'; import { Link, Tabs } from "expo-router";
import { Pressable } from 'react-native'; import { Pressable } from "react-native";
import Colors from "@/constants/Colors";
import Colors from '@/constants/Colors'; import { useColorScheme } from "@/components/useColorScheme";
import { useColorScheme } from '@/components/useColorScheme'; import { useClientOnlyValue } from "@/components/useClientOnlyValue";
import { useClientOnlyValue } from '@/components/useClientOnlyValue'; import { TAB_CONFIG } from "@/constants/config";
// You can explore the built-in icon families and icons on the web at https://icons.expo.fyi/ // You can explore the built-in icon families and icons on the web at https://icons.expo.fyi/
function TabBarIcon(props: { function TabBarIcon(props: {
@ -21,9 +21,7 @@ export default function TabLayout() {
return ( return (
<Tabs <Tabs
screenOptions={{ screenOptions={{
tabBarActiveTintColor: Colors[colorScheme ?? 'light'].tint, tabBarActiveTintColor: Colors[colorScheme ?? "light"].tint,
// Disable the static render of the header on web
// to prevent a hydration error in React Navigation v6.
headerShown: useClientOnlyValue(false, true), headerShown: useClientOnlyValue(false, true),
}}> }}>
<Tabs.Screen <Tabs.Screen

View File

@ -1,21 +1,27 @@
import FontAwesome from '@expo/vector-icons/FontAwesome'; import i18next, { getLanguage } from "../services/i18n/index";
import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native'; import FontAwesome from "@expo/vector-icons/FontAwesome";
import { useFonts } from 'expo-font'; import {
import { Stack } from 'expo-router'; DarkTheme,
import * as SplashScreen from 'expo-splash-screen'; DefaultTheme,
import { useEffect } from 'react'; ThemeProvider,
import 'react-native-reanimated'; } from "@react-navigation/native";
import { useFonts } from "expo-font";
import { Stack, useRouter } from "expo-router";
import * as SplashScreen from "expo-splash-screen";
import { useEffect, useState } from "react";
import "react-native-reanimated";
import { I18nextProvider } from "react-i18next"; //I18nextProvider
import { useColorScheme } from '@/components/useColorScheme'; import { useColorScheme } from "@/components/useColorScheme";
export { export {
// Catch any errors thrown by the Layout component. // Catch any errors thrown by the Layout component.
ErrorBoundary, ErrorBoundary,
} from 'expo-router'; } from "expo-router";
export const unstable_settings = { export const unstable_settings = {
// Ensure that reloading on `/modal` keeps a back button present. // Ensure that reloading on `/modal` keeps a back button present.
initialRouteName: '(tabs)', initialRouteName: "(tabs)",
}; };
// Prevent the splash screen from auto-hiding before asset loading is complete. // Prevent the splash screen from auto-hiding before asset loading is complete.
@ -23,7 +29,7 @@ SplashScreen.preventAutoHideAsync();
export default function RootLayout() { export default function RootLayout() {
const [loaded, error] = useFonts({ const [loaded, error] = useFonts({
SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'), SpaceMono: require("../assets/fonts/SpaceMono-Regular.ttf"),
...FontAwesome.font, ...FontAwesome.font,
}); });
@ -49,11 +55,13 @@ function RootLayoutNav() {
const colorScheme = useColorScheme(); const colorScheme = useColorScheme();
return ( return (
<ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}> <I18nextProvider i18n={i18next}>
<ThemeProvider value={colorScheme === "dark" ? DarkTheme : DefaultTheme}>
<Stack> <Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} /> <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="modal" options={{ presentation: 'modal' }} /> <Stack.Screen name="modal" options={{ presentation: "modal" }} />
</Stack> </Stack>
</ThemeProvider> </ThemeProvider>
</I18nextProvider>
); );
} }

40
services/i18n/index.ts Normal file
View File

@ -0,0 +1,40 @@
import i18next from "i18next";
import { initReactI18next } from "react-i18next";
import en from "../i18n/locals/en.json";
import hi from "../i18n/locals/hi.json";
import AsyncStorage from "@react-native-async-storage/async-storage";
import { STORAGE_KEYS } from "../../constants/config";
export const languageResources = {
en: { translation: en },
hi: { translation: hi },
};
i18next.use(initReactI18next).init({
compatibilityJSON: "v4",
lng: "hi",
fallbackLng: "en",
resources: languageResources,
debug: true,
});
i18next.on("initialized", () => {
console.log("✅ i18next initialized with language:", i18next.language);
console.log("Translation test (welcome):", i18next.t("welcome"));
});
export const setLanguage = async (language: string) => {
await AsyncStorage.setItem(STORAGE_KEYS.LANGUAGE, language);
i18next.changeLanguage(language);
};
export const getLanguage = async () => {
const lang = await AsyncStorage.getItem(STORAGE_KEYS.LANGUAGE);
if (lang) {
i18next.changeLanguage(lang);
}
return lang;
};
export default i18next;

View File

@ -0,0 +1,8 @@
{
"en": {
"name": "English"
},
"hi": {
"name": "हिंदी"
}
}

View File

@ -0,0 +1,4 @@
{
"change-language": "Change language",
"welcome": "Welcome to Driver Saathi"
}

View File

@ -0,0 +1,4 @@
{
"change-language": "भाषा बदलें",
"welcome": "ड्राइवर साथी में आपका स्वागत है"
}