44
loading...
This website collects cookies to deliver better user experience
//App.tsx
import { StatusBar } from "expo-status-bar";
import React from "react";
import { StyleSheet, Text, View, Button } from "react-native";
export default function App() {
return (
<View style={styles.container}>
<Text style={styles.text}>Hello!</Text>
<Button title="Press" onPress={() => Alert.alert("HELLO")} />
<StatusBar style="auto" />
</View>
);
}
//styles omitted
npm i react-i18next i18next
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
//empty for now
const resources = {};
i18n.use(initReactI18next).init({
resources,
//language to use if translations in user language are not available
fallbackLng: "en",
interpolation: {
escapeValue: false, // not needed for react!!
},
});
export default i18n;
//App.tsx
import { StatusBar } from "expo-status-bar";
import React from "react";
import { StyleSheet, Text, View, Button } from "react-native";
import "./i18n.config"; // <-- this line added
export default function App() {
return (
<View style={styles.container}>
<Text style={styles.text}>Hello!</Text>
<Button title="Press" onPress={() => Alert.alert("HELLO")} />
<StatusBar style="auto" />
</View>
);
}
//translations folder structure
├── translations/
│ ├── be.json
│ ├── de.json
│ ├── en.json
│ ├── es.json
│ ├── fr.json
npm i google-spreadsheet
{
"type": "service_account",
"project_id": "XXXXXXXXXXXXXXX",
"private_key_id": "XXXXXXXXXXXXXXX",
"private_key": "XXXXXXXXXXXXXXX",
"client_email": "service-account-google-sheet-a@XXXXXXXXXXXX.iam.gserviceaccount.com",
"client_id": "XXXXXXXXXXXXXXX",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/service-account-google-sheet-XXXXXXXXXXXXX.iam.gserviceaccount.com"
}
//utils/script.js
const { GoogleSpreadsheet } = require("google-spreadsheet");
const secret = require("./secret.json");
// Initialize the sheet
const doc = new GoogleSpreadsheet("<YOUR_GOOGLE_SHEET_ID");
// Initialize Auth
const init = async () => {
await doc.useServiceAccountAuth({
client_email: secret.client_email, //don't forget to share the Google sheet with your service account using your client_email value
private_key: secret.private_key,
});
};
https://docs.google.com/spreadsheets/d/spreadsheetId/edit#gid=0
1hDB6qlijcU5iovtSAisKqkcXhdVboFd1lg__maKwvDI
//utils/script.js
...
const read = async () => {
await doc.loadInfo(); // loads document properties and worksheets
const sheet = doc.sheetsByTitle.Sheet1; //get the sheet by title, I left the default title name. If you changed it, then you should use the name of your sheet
await sheet.loadHeaderRow(); //Loads the header row (first row) of the sheet
const colTitles = sheet.headerValues; //array of strings from cell values in the first row
const rows = await sheet.getRows({ limit: sheet.rowCount }); //fetch rows from the sheet (limited to row count)
let result = {};
//map rows values and create an object with keys as columns titles starting from the second column (languages names) and values as an object with key value pairs, where the key is a key of translation, and value is a translation in a respective language
rows.map((row) => {
colTitles.slice(1).forEach((title) => {
result[title] = result[title] || [];
const key = row[colTitles[0]];
result = {
...result,
[title]: {
...result[title],
[key]: row[title] !== "" ? row[title] : undefined,
},
};
});
});
return result;
};
cd utils && node script.js
{
en: { HELLO: 'Hello', PRESS: 'Press' },
fr: { HELLO: 'Bonjour', PRESS: 'Presse' },
es: { HELLO: 'Hola', PRESS: 'Prensa' },
de: { HELLO: 'Hallo', PRESS: 'Drücken Sie' },
be: { HELLO: 'Прывітанне', PRESS: 'Прэс' }
}
//utils/script.js
...
const fs = require("fs");
...
const write = (data) => {
Object.keys(data).forEach((key) => {
fs.writeFile(
`../translations/${key}.json`,
JSON.stringify(data[key], null, 2),
(err) => {
if (err) {
console.error(err);
}
}
);
});
};
//utils/script.js
...
init()
.then(() => read())
.then((data) => write(data))
.catch((err) => console.log("ERROR!!!!", err));
node script.js
//utils/index.js
export { default as be } from "./be.json";
export { default as en } from "./en.json";
export { default as de } from "./de.json";
export { default as es } from "./es.json";
export { default as fr } from "./fr.json";
//i18n.config.ts
...
import { en, be, fr, de, es } from "./translations";
const resources = {
en: {
translation: en,
},
de: {
translation: de,
},
es: {
translation: es,
},
be: {
translation: be,
},
fr: {
translation: fr,
},
};
...
//App.tsx
...
import { useTranslation } from "react-i18next";
export default function App() {
const { t } = useTranslation();
return (
<View style={styles.container}>
<Text style={styles.text}>{`${t("HELLO")}!`}</Text>
<Button title={t("PRESS")} onPress={() => Alert.alert(t("HELLO"))} />
<StatusBar style="auto" />
</View>
);
}
//styles omitted
//LanguagePicker.tsx
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { Modal, View, Text, Pressable, StyleSheet } from "react-native";
const LanguagePicker = () => {
const [modalVisible, setModalVisible] = useState(false);
const { i18n } = useTranslation(); //i18n instance
//array with all supported languages
const languages = [
{ name: "de", label: "Deutsch" },
{ name: "en", label: "English" },
{ name: "fr", label: "Français" },
{ name: "be", label: "Беларуская" },
{ name: "es", label: "Español" },
];
const LanguageItem = ({ name, label }: { name: string; label: string }) => (
<Pressable
style={styles.button}
onPress={() => {
i18n.changeLanguage(name); //changes the app language
setModalVisible(!modalVisible);
}}
>
<Text style={styles.textStyle}>{label}</Text>
</Pressable>
);
return (
<View>
<Modal
animationType="slide"
transparent={true}
visible={modalVisible}
onRequestClose={() => {
setModalVisible(!modalVisible);
}}
>
<View style={styles.centeredView}>
<View style={styles.modalView}>
{languages.map((lang) => (
<LanguageItem {...lang} key={lang.name} />
))}
</View>
</View>
</Modal>
<Pressable
style={[styles.button, styles.buttonOpen]}
onPress={() => setModalVisible(true)}
>
//displays the current app language
<Text style={styles.textStyle}>{i18n.language}</Text>
</Pressable>
</View>
);
};
export default LanguagePicker;
//styles omitted
//App.tsx
import { StatusBar } from "expo-status-bar";
import React from "react";
import { StyleSheet, Text, View, Button } from "react-native";
import "./i18n.config";
import { useTranslation } from "react-i18next";
import LanguagePicker from "./LanguagePicker";
export default function App() {
const { t } = useTranslation();
return (
<View style={styles.container}>
<LanguagePicker />
<Text style={styles.text}>{`${t("HELLO")}!`}</Text>
<Button title={t("PRESS")} onPress={() => Alert.alert(t("HELLO"))} />
<StatusBar style="auto" />
</View>
);
}
expo install @react-native-async-storage/async-storage
npm i @react-native-async-storage/async-storage
npx pod-install
expo install expo-localization
//utils/languageDetectorPlugin.ts
import AsyncStorage from "@react-native-async-storage/async-storage";
import * as Localization from "expo-localization";
const STORE_LANGUAGE_KEY = "settings.lang";
const languageDetectorPlugin = {
type: "languageDetector",
async: true,
init: () => {},
detect: async function (callback: (lang: string) => void) {
try {
//get stored language from Async storage
await AsyncStorage.getItem(STORE_LANGUAGE_KEY).then((language) => {
if (language) {
//if language was stored before, use this language in the app
return callback(language);
} else {
//if language was not stored yet, use device's locale
return callback(Localization.locale);
}
});
} catch (error) {
console.log("Error reading language", error);
}
},
cacheUserLanguage: async function (language: string) {
try {
//save a user's language choice in Async storage
await AsyncStorage.setItem(STORE_LANGUAGE_KEY, language);
} catch (error) {}
},
};
module.exports = { languageDetectorPlugin };
//i18n.config.ts
...
const { languageDetectorPlugin } = require("./utils/languageDetectorPlugin");
...
i18n
.use(initReactI18next)
.use(languageDetectorPlugin)
.init({
resources,
//language to use if translations in user language are not available
fallbackLng: "en",
interpolation: {
escapeValue: false, // not needed for react!!
},
react: {
useSuspense: false, //in case you have any suspense related errors
},
});
...