Internationalize Bible UI and add locale-aware translation fetch

This commit is contained in:
Adolfo Reyna
2026-02-24 16:04:32 -05:00
parent ccfeed3c92
commit fc3159d3fb
6 changed files with 136 additions and 34 deletions

View File

@@ -3,8 +3,9 @@ import { FlatList, Pressable, View } from "react-native";
import { ActivityIndicator, Text } from "react-native-paper";
import { SafeAreaView } from "react-native-safe-area-context";
import { useNavigation } from "@react-navigation/native";
import { parseBibleReference } from "../utils/bibleReferences.js";
import { fetchBibleChapter, parseBibleReference } from "../utils/bibleReferences.js";
import GlobalState from "../contexts/GlobalState.js";
import i18n from "../i18nMessages.js";
const BibleChapterView = ({ route }) => {
const navigation = useNavigation();
@@ -23,14 +24,12 @@ const BibleChapterView = ({ route }) => {
setLoading(true);
setError("");
try {
const response = await fetch(`https://bible-api.com/${encodeURIComponent(chapterReference)}`);
if (!response.ok) throw new Error("Failed chapter request");
const payload = await response.json();
const payload = await fetchBibleChapter(chapterReference, i18n.locale);
if (!mounted) return;
setChapterData(payload);
} catch (_error) {
if (!mounted) return;
setError("Unable to load chapter.");
setError(i18n.t("message.unableLoadChapter"));
setChapterData(null);
} finally {
if (mounted) setLoading(false);
@@ -100,7 +99,7 @@ const BibleChapterView = ({ route }) => {
{chapterData?.translation_name || chapterData?.translation_id || "KJV"}
</Text>
{selectable ? (
<Text style={{ color: "#6b7280", marginBottom: 8 }}>Tap a verse to select it.</Text>
<Text style={{ color: "#6b7280", marginBottom: 8 }}>{i18n.t("message.tapVerseToSelect")}</Text>
) : null}
<FlatList
ref={listRef}

View File

@@ -4,7 +4,8 @@ import { ActivityIndicator, Button, Chip, Divider, Text, TextInput } from "react
import { useNavigation } from "@react-navigation/native";
import { useSnapshot } from "valtio";
import GlobalState from "../contexts/GlobalState.js";
import { BIBLE_BOOKS, createBibleToken, fetchBiblePassage, getBookChapterCount } from "../utils/bibleReferences.js";
import { BIBLE_BOOKS, createBibleToken, fetchBibleChapter, fetchBiblePassage, getBookChapterCount } from "../utils/bibleReferences.js";
import i18n from "../i18nMessages.js";
const BiblePicker = ({ route }) => {
const navigation = useNavigation();
@@ -57,11 +58,11 @@ const BiblePicker = ({ route }) => {
setError("");
setLoadingPreview(true);
try {
const passage = await fetchBiblePassage(reference);
const passage = await fetchBiblePassage(reference, i18n.locale);
setPreview(passage);
} catch (_err) {
setPreview(null);
setError("Unable to load this Bible passage.");
setError(i18n.t("message.unableLoadPassage"));
} finally {
setLoadingPreview(false);
}
@@ -74,9 +75,7 @@ const BiblePicker = ({ route }) => {
}
setLoadingVerses(true);
try {
const response = await fetch(`https://bible-api.com/${encodeURIComponent(`${book} ${chapterNumber}`)}`);
if (!response.ok) throw new Error("Failed chapter fetch");
const payload = await response.json();
const payload = await fetchBibleChapter(`${book} ${chapterNumber}`, i18n.locale);
const options = Array.isArray(payload?.verses)
? payload.verses.map((v) => String(v?.verse || "")).filter(Boolean)
: [];
@@ -103,9 +102,9 @@ const BiblePicker = ({ route }) => {
return (
<View style={{ flex: 1, paddingHorizontal: 12, paddingTop: 12 }}>
<Text style={{ fontSize: 22, fontWeight: "700", marginBottom: 6 }}>Bible Reference</Text>
<Text style={{ fontSize: 22, fontWeight: "700", marginBottom: 6 }}>{i18n.t("message.bibleReferenceTitle")}</Text>
<Text style={{ color: "#6b7280", marginBottom: 10 }}>
Pick a reference to insert into your {target === "chat" ? "chat message" : "post"}.
{target === "chat" ? i18n.t("message.biblePickerSubtitleChat") : i18n.t("message.biblePickerSubtitlePost")}
</Text>
<TextInput
@@ -117,14 +116,14 @@ const BiblePicker = ({ route }) => {
/>
<View style={{ flexDirection: "row", marginTop: 8, justifyContent: "space-between" }}>
<Button mode="outlined" onPress={() => loadPreviewForReference(computedReference)} loading={loadingPreview}>
Preview
{i18n.t("message.preview")}
</Button>
<Button mode="contained" disabled={!computedReference} onPress={() => addReferenceToCaller(computedReference)}>
Use Reference
{i18n.t("message.useReference")}
</Button>
</View>
{computedReference ? <Text style={{ marginTop: 8, color: "#374151" }}>Selected: {computedReference}</Text> : null}
{computedReference ? <Text style={{ marginTop: 8, color: "#374151" }}>{i18n.t("message.selected")}: {computedReference}</Text> : null}
{preview?.text ? (
<Pressable
onPress={() => navigation.navigate("BibleChapter", { reference: computedReference, selectable: true })}
@@ -134,7 +133,7 @@ const BiblePicker = ({ route }) => {
<Text style={{ marginTop: 4, color: "#4b5563", fontSize: 12 }}>
{preview.reference} ({preview.translation})
</Text>
<Text style={{ marginTop: 4, color: "#6b7280", fontSize: 12 }}>Tap preview to pick verse from chapter</Text>
<Text style={{ marginTop: 4, color: "#6b7280", fontSize: 12 }}>{i18n.t("message.tapPreviewPickVerse")}</Text>
</Pressable>
) : null}
{error ? <Text style={{ marginTop: 8, color: "#b91c1c" }}>{error}</Text> : null}
@@ -143,27 +142,27 @@ const BiblePicker = ({ route }) => {
<View style={{ flexDirection: "row", marginBottom: 10 }}>
<Button mode={activeStep === "book" ? "contained-tonal" : "text"} onPress={() => setActiveStep("book")}>
Book
{i18n.t("message.book")}
</Button>
<Button
mode={activeStep === "chapter" ? "contained-tonal" : "text"}
disabled={!selectedBook}
onPress={() => setActiveStep("chapter")}
>
Chapter
{i18n.t("message.chapter")}
</Button>
<Button
mode={activeStep === "verse" ? "contained-tonal" : "text"}
disabled={!selectedBook || !chapter}
onPress={() => setActiveStep("verse")}
>
Verse
{i18n.t("message.verse")}
</Button>
</View>
{activeStep === "book" ? (
<>
<Text style={{ fontSize: 14, fontWeight: "600", marginBottom: 8 }}>Books</Text>
<Text style={{ fontSize: 14, fontWeight: "600", marginBottom: 8 }}>{i18n.t("message.books")}</Text>
<FlatList
data={filteredBooks}
numColumns={2}
@@ -192,7 +191,7 @@ const BiblePicker = ({ route }) => {
{activeStep === "chapter" ? (
<>
<Text style={{ fontSize: 14, fontWeight: "600", marginBottom: 8 }}>
Chapters {selectedBook ? `(${selectedBook})` : ""}
{i18n.t("message.chapters")} {selectedBook ? `(${selectedBook})` : ""}
</Text>
<FlatList
data={chapterOptions}
@@ -220,7 +219,7 @@ const BiblePicker = ({ route }) => {
{activeStep === "verse" ? (
<>
<Text style={{ fontSize: 14, fontWeight: "600", marginBottom: 8 }}>
Verses {selectedBook && chapter ? `(${selectedBook} ${chapter})` : ""}
{i18n.t("message.verses")} {selectedBook && chapter ? `(${selectedBook} ${chapter})` : ""}
</Text>
{loadingVerses ? (
<ActivityIndicator style={{ marginTop: 8 }} />

View File

@@ -248,7 +248,7 @@ let NewPostView = (props) => {
mode="outlined"
onPress={() => navigation.navigate("BiblePicker", { target: "post" })}
>
Bible
{i18n.t("message.bible")}
</Button>
<Button icon="add-a-photo" mode="outlined" onPress={pickImage}>
{i18n.t("message.addPhotos")}