Skip to content

Commit 6c6f468

Browse files
authored
Merge pull request #9 from daboigbae/enhancement/add-localization
Added localization
2 parents 472f6a3 + eab9a07 commit 6c6f468

11 files changed

Lines changed: 133 additions & 14 deletions

File tree

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,13 @@ Run this command to create a new project using the template
1414

1515
# 📦Installed packages
1616
- [axios](https://github.com/axios/axios) for networking.
17+
- [i18n-js](https://www.npmjs.com/package/i18n-js) small library to provide the i18n translations with JavaScript
18+
- [lodash](https://lodash.com/) modern Javascript library delivering modularity and performance
1719
- [lottie-react-native](https://github.com/lottie-react-native/lottie-react-native) an ecosystem of libraries for parsing Adobe After Effects animations exported as JSON
20+
- [moment](https://momentjs.com/) library to parse, validate, manipulate and display date/time in JavaScript
1821
- [Munchkin](https://munchkinreact.app/) a react native component library
1922
- [nativewind](https://www.nativewind.dev/) a universal style system
23+
- [react-native-localize](https://www.npmjs.com/package/react-native-localize) a toolbox for your React Native app localization
2024
- [react-native-vector-icons](https://github.com/oblador/react-native-vector-icons) an icon library.
2125
- [react-navigation](https://reactnavigation.org/) navigation library.
2226
- [redux](https://redux.js.org/) for state management.

template/App.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,18 @@ import { persistStore } from "redux-persist";
88
import Navigation from "./src/navigation/Navigation";
99

1010
import store from "./src/redux/store";
11+
import useLocalize from "./src/hooks/useLocalize";
12+
import { useEffect } from "react";
1113

1214
const persistor = persistStore(store);
1315

1416
const App = () => {
17+
const { setI18nConfig } = useLocalize();
18+
19+
useEffect(() => {
20+
setI18nConfig();
21+
}, [setI18nConfig]);
22+
1523
return (
1624
<Provider store={store}>
1725
<PersistGate persistor={persistor}>

template/ios/Podfile.lock

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,8 @@ PODS:
364364
- React-Core
365365
- RNGestureHandler (2.6.0):
366366
- React-Core
367+
- RNLocalize (2.2.4):
368+
- React-Core
367369
- RNReanimated (2.10.0):
368370
- DoubleConversion
369371
- FBLazyVector
@@ -461,6 +463,7 @@ DEPENDENCIES:
461463
- ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
462464
- "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)"
463465
- RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
466+
- RNLocalize (from `../node_modules/react-native-localize`)
464467
- RNReanimated (from `../node_modules/react-native-reanimated`)
465468
- RNScreens (from `../node_modules/react-native-screens`)
466469
- RNVectorIcons (from `../node_modules/react-native-vector-icons`)
@@ -556,6 +559,8 @@ EXTERNAL SOURCES:
556559
:path: "../node_modules/@react-native-async-storage/async-storage"
557560
RNGestureHandler:
558561
:path: "../node_modules/react-native-gesture-handler"
562+
RNLocalize:
563+
:path: "../node_modules/react-native-localize"
559564
RNReanimated:
560565
:path: "../node_modules/react-native-reanimated"
561566
RNScreens:
@@ -615,6 +620,7 @@ SPEC CHECKSUMS:
615620
ReactCommon: 8f67bd7e0a6afade0f20718f859dc8c2275f2e83
616621
RNCAsyncStorage: 0c357f3156fcb16c8589ede67cc036330b6698ca
617622
RNGestureHandler: 920eb17f5b1e15dae6e5ed1904045f8f90e0b11e
623+
RNLocalize: 0df7970cfc60389f00eb62fd7c097dc75af3fb4f
618624
RNReanimated: 7faa787e8d4493fbc95fab2ad331fa7625828cfa
619625
RNScreens: 0df01424e9e0ed7827200d6ed1087ddd06c493f9
620626
RNVectorIcons: fcc2f6cb32f5735b586e66d14103a74ce6ad61f8

template/package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,17 @@
2929
"@reduxjs/toolkit": "^1.8.5",
3030
"add": "^2.0.6",
3131
"axios": "^0.27.2",
32+
"i18n-js": "3.9.2",
33+
"lodash": "^4.17.21",
34+
"lodash.memoize": "4.1.2",
3235
"lottie-react-native": "^5.1.4",
36+
"moment": "^2.29.4",
3337
"nativewind": "^2.0.10",
3438
"react": "18.0.0",
3539
"react-native": "0.69.4",
3640
"react-native-gesture-handler": "^2.6.0",
3741
"react-native-keyboard-aware-scroll-view": "^0.9.5",
42+
"react-native-localize": "^2.2.4",
3843
"react-native-reanimated": "^2.10.0",
3944
"react-native-safe-area-context": "^4.3.3",
4045
"react-native-screens": "^3.16.0",

template/src/hooks/useLocalize.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { I18nManager } from "react-native";
2+
import * as RNLocalize from "react-native-localize";
3+
4+
import i18n from "i18n-js";
5+
import memoize from "lodash.memoize";
6+
7+
const translationGetters = {
8+
en: () => require("../utils/translations/en.json"),
9+
es: () => require("../utils/translations/es.json")
10+
// add lanugauges you want to support here
11+
};
12+
13+
const translate = memoize(
14+
(key, config) => i18n.t(key, config),
15+
(key, config) => (config ? key + JSON.stringify(config) : key)
16+
);
17+
18+
export default function useLocalize() {
19+
const setI18nConfig = () => {
20+
const fallback = { languageTag: "en", isRTL: false };
21+
22+
const { languageTag, isRTL } =
23+
RNLocalize.findBestAvailableLanguage(Object.keys(translationGetters)) ||
24+
fallback;
25+
26+
translate.cache.clear();
27+
28+
I18nManager.forceRTL(isRTL);
29+
30+
i18n.translations = { [languageTag]: translationGetters[languageTag]() };
31+
32+
i18n.locale = languageTag;
33+
};
34+
35+
return { setI18nConfig, translate };
36+
}

template/src/navigation/BottomTabNavigation.js

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,33 @@ import React from "react";
22
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
33
import PropTypes from "prop-types";
44

5-
import { BOTTOM_TAB_SCREENS } from "../utils/screens";
5+
import useLocalize from "../hooks/useLocalize";
66
import HomeScreen from "../screens/HomeScreen";
7+
import { BOTTOM_TAB_SCREENS } from "../utils/screens";
8+
import { TRANSLATIONS } from "../utils/translations/translations";
79

810
const Tab = createBottomTabNavigator();
911

10-
const BottomTabNavigation = () => (
11-
<Tab.Navigator
12-
screenOptions={() => ({
13-
drawerPosition: "right",
14-
headerLeft: false
15-
})}>
16-
<Tab.Screen name={BOTTOM_TAB_SCREENS.HOME_SCREEN} component={HomeScreen} />
17-
</Tab.Navigator>
18-
);
12+
const TAB_SCREEN_OPTIONS = {
13+
drawerPosition: "right",
14+
headerLeft: false
15+
};
16+
17+
const BottomTabNavigation = () => {
18+
const { translate } = useLocalize();
19+
20+
return (
21+
<Tab.Navigator screenOptions={TAB_SCREEN_OPTIONS}>
22+
<Tab.Screen
23+
name={BOTTOM_TAB_SCREENS.HOME_SCREEN}
24+
component={HomeScreen}
25+
options={{
26+
tabBarLabel: translate(TRANSLATIONS.MAIN)
27+
}}
28+
/>
29+
</Tab.Navigator>
30+
);
31+
};
1932

2033
BottomTabNavigation.propTypes = {
2134
navigation: PropTypes.object

template/src/screens/HomeScreen.js

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,36 @@
11
import React from "react";
2-
import { View } from "react-native";
3-
2+
import { Text, View } from "react-native";
3+
import moment from "moment";
44
import { Button } from "@digital-art-dealers/react-native-component-lib";
5+
6+
import useLocalize from "../hooks/useLocalize";
7+
import { TRANSLATIONS } from "../utils/translations/translations";
8+
import { useLayoutEffect } from "react";
9+
import { useNavigation } from "@react-navigation/native";
10+
511
const HomeScreen = () => {
12+
const navigation = useNavigation();
13+
const { translate } = useLocalize();
14+
15+
// TODO: This can be removed or changed according to project needs
16+
useLayoutEffect(() => {
17+
navigation.setOptions({
18+
headerTitle: translate(TRANSLATIONS.MAIN)
19+
});
20+
}, [navigation]);
21+
622
return (
723
<View className="h-full w-full justify-center px-4">
824
<Button
925
buttonColor="bg-blue-600"
1026
textColor="text-white"
1127
onPress={() => {}}
12-
label="Change this button"
28+
label={translate(TRANSLATIONS.CHANGE_BUTTON)}
1329
/>
30+
<Text className="text-2xl font-bold text-center mt-8">Todays Date</Text>
31+
<Text className="text-center text-lg">
32+
{moment(new Date()).format("YYYY-MM-DD")}
33+
</Text>
1434
</View>
1535
);
1636
};
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"changeButton": "Change this button",
3+
"main": "Main"
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"changeButton": "Cambiar este botón",
3+
"main": "Principal"
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export const TRANSLATIONS = {
2+
CHANGE_BUTTON: "changeButton",
3+
MAIN: "main"
4+
};

0 commit comments

Comments
 (0)