Internationalize Bible UI and add locale-aware translation fetch
This commit is contained in:
@@ -3,8 +3,9 @@ import { FlatList, Pressable, View } from "react-native";
|
|||||||
import { ActivityIndicator, Text } from "react-native-paper";
|
import { ActivityIndicator, Text } from "react-native-paper";
|
||||||
import { SafeAreaView } from "react-native-safe-area-context";
|
import { SafeAreaView } from "react-native-safe-area-context";
|
||||||
import { useNavigation } from "@react-navigation/native";
|
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 GlobalState from "../contexts/GlobalState.js";
|
||||||
|
import i18n from "../i18nMessages.js";
|
||||||
|
|
||||||
const BibleChapterView = ({ route }) => {
|
const BibleChapterView = ({ route }) => {
|
||||||
const navigation = useNavigation();
|
const navigation = useNavigation();
|
||||||
@@ -23,14 +24,12 @@ const BibleChapterView = ({ route }) => {
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError("");
|
setError("");
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`https://bible-api.com/${encodeURIComponent(chapterReference)}`);
|
const payload = await fetchBibleChapter(chapterReference, i18n.locale);
|
||||||
if (!response.ok) throw new Error("Failed chapter request");
|
|
||||||
const payload = await response.json();
|
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
setChapterData(payload);
|
setChapterData(payload);
|
||||||
} catch (_error) {
|
} catch (_error) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
setError("Unable to load chapter.");
|
setError(i18n.t("message.unableLoadChapter"));
|
||||||
setChapterData(null);
|
setChapterData(null);
|
||||||
} finally {
|
} finally {
|
||||||
if (mounted) setLoading(false);
|
if (mounted) setLoading(false);
|
||||||
@@ -100,7 +99,7 @@ const BibleChapterView = ({ route }) => {
|
|||||||
{chapterData?.translation_name || chapterData?.translation_id || "KJV"}
|
{chapterData?.translation_name || chapterData?.translation_id || "KJV"}
|
||||||
</Text>
|
</Text>
|
||||||
{selectable ? (
|
{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}
|
) : null}
|
||||||
<FlatList
|
<FlatList
|
||||||
ref={listRef}
|
ref={listRef}
|
||||||
|
|||||||
+17
-18
@@ -4,7 +4,8 @@ import { ActivityIndicator, Button, Chip, Divider, Text, TextInput } from "react
|
|||||||
import { useNavigation } from "@react-navigation/native";
|
import { useNavigation } from "@react-navigation/native";
|
||||||
import { useSnapshot } from "valtio";
|
import { useSnapshot } from "valtio";
|
||||||
import GlobalState from "../contexts/GlobalState.js";
|
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 BiblePicker = ({ route }) => {
|
||||||
const navigation = useNavigation();
|
const navigation = useNavigation();
|
||||||
@@ -57,11 +58,11 @@ const BiblePicker = ({ route }) => {
|
|||||||
setError("");
|
setError("");
|
||||||
setLoadingPreview(true);
|
setLoadingPreview(true);
|
||||||
try {
|
try {
|
||||||
const passage = await fetchBiblePassage(reference);
|
const passage = await fetchBiblePassage(reference, i18n.locale);
|
||||||
setPreview(passage);
|
setPreview(passage);
|
||||||
} catch (_err) {
|
} catch (_err) {
|
||||||
setPreview(null);
|
setPreview(null);
|
||||||
setError("Unable to load this Bible passage.");
|
setError(i18n.t("message.unableLoadPassage"));
|
||||||
} finally {
|
} finally {
|
||||||
setLoadingPreview(false);
|
setLoadingPreview(false);
|
||||||
}
|
}
|
||||||
@@ -74,9 +75,7 @@ const BiblePicker = ({ route }) => {
|
|||||||
}
|
}
|
||||||
setLoadingVerses(true);
|
setLoadingVerses(true);
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`https://bible-api.com/${encodeURIComponent(`${book} ${chapterNumber}`)}`);
|
const payload = await fetchBibleChapter(`${book} ${chapterNumber}`, i18n.locale);
|
||||||
if (!response.ok) throw new Error("Failed chapter fetch");
|
|
||||||
const payload = await response.json();
|
|
||||||
const options = Array.isArray(payload?.verses)
|
const options = Array.isArray(payload?.verses)
|
||||||
? payload.verses.map((v) => String(v?.verse || "")).filter(Boolean)
|
? payload.verses.map((v) => String(v?.verse || "")).filter(Boolean)
|
||||||
: [];
|
: [];
|
||||||
@@ -103,9 +102,9 @@ const BiblePicker = ({ route }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={{ flex: 1, paddingHorizontal: 12, paddingTop: 12 }}>
|
<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 }}>
|
<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>
|
</Text>
|
||||||
|
|
||||||
<TextInput
|
<TextInput
|
||||||
@@ -117,14 +116,14 @@ const BiblePicker = ({ route }) => {
|
|||||||
/>
|
/>
|
||||||
<View style={{ flexDirection: "row", marginTop: 8, justifyContent: "space-between" }}>
|
<View style={{ flexDirection: "row", marginTop: 8, justifyContent: "space-between" }}>
|
||||||
<Button mode="outlined" onPress={() => loadPreviewForReference(computedReference)} loading={loadingPreview}>
|
<Button mode="outlined" onPress={() => loadPreviewForReference(computedReference)} loading={loadingPreview}>
|
||||||
Preview
|
{i18n.t("message.preview")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button mode="contained" disabled={!computedReference} onPress={() => addReferenceToCaller(computedReference)}>
|
<Button mode="contained" disabled={!computedReference} onPress={() => addReferenceToCaller(computedReference)}>
|
||||||
Use Reference
|
{i18n.t("message.useReference")}
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</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 ? (
|
{preview?.text ? (
|
||||||
<Pressable
|
<Pressable
|
||||||
onPress={() => navigation.navigate("BibleChapter", { reference: computedReference, selectable: true })}
|
onPress={() => navigation.navigate("BibleChapter", { reference: computedReference, selectable: true })}
|
||||||
@@ -134,7 +133,7 @@ const BiblePicker = ({ route }) => {
|
|||||||
<Text style={{ marginTop: 4, color: "#4b5563", fontSize: 12 }}>
|
<Text style={{ marginTop: 4, color: "#4b5563", fontSize: 12 }}>
|
||||||
{preview.reference} ({preview.translation})
|
{preview.reference} ({preview.translation})
|
||||||
</Text>
|
</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>
|
</Pressable>
|
||||||
) : null}
|
) : null}
|
||||||
{error ? <Text style={{ marginTop: 8, color: "#b91c1c" }}>{error}</Text> : null}
|
{error ? <Text style={{ marginTop: 8, color: "#b91c1c" }}>{error}</Text> : null}
|
||||||
@@ -143,27 +142,27 @@ const BiblePicker = ({ route }) => {
|
|||||||
|
|
||||||
<View style={{ flexDirection: "row", marginBottom: 10 }}>
|
<View style={{ flexDirection: "row", marginBottom: 10 }}>
|
||||||
<Button mode={activeStep === "book" ? "contained-tonal" : "text"} onPress={() => setActiveStep("book")}>
|
<Button mode={activeStep === "book" ? "contained-tonal" : "text"} onPress={() => setActiveStep("book")}>
|
||||||
Book
|
{i18n.t("message.book")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
mode={activeStep === "chapter" ? "contained-tonal" : "text"}
|
mode={activeStep === "chapter" ? "contained-tonal" : "text"}
|
||||||
disabled={!selectedBook}
|
disabled={!selectedBook}
|
||||||
onPress={() => setActiveStep("chapter")}
|
onPress={() => setActiveStep("chapter")}
|
||||||
>
|
>
|
||||||
Chapter
|
{i18n.t("message.chapter")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
mode={activeStep === "verse" ? "contained-tonal" : "text"}
|
mode={activeStep === "verse" ? "contained-tonal" : "text"}
|
||||||
disabled={!selectedBook || !chapter}
|
disabled={!selectedBook || !chapter}
|
||||||
onPress={() => setActiveStep("verse")}
|
onPress={() => setActiveStep("verse")}
|
||||||
>
|
>
|
||||||
Verse
|
{i18n.t("message.verse")}
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{activeStep === "book" ? (
|
{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
|
<FlatList
|
||||||
data={filteredBooks}
|
data={filteredBooks}
|
||||||
numColumns={2}
|
numColumns={2}
|
||||||
@@ -192,7 +191,7 @@ const BiblePicker = ({ route }) => {
|
|||||||
{activeStep === "chapter" ? (
|
{activeStep === "chapter" ? (
|
||||||
<>
|
<>
|
||||||
<Text style={{ fontSize: 14, fontWeight: "600", marginBottom: 8 }}>
|
<Text style={{ fontSize: 14, fontWeight: "600", marginBottom: 8 }}>
|
||||||
Chapters {selectedBook ? `(${selectedBook})` : ""}
|
{i18n.t("message.chapters")} {selectedBook ? `(${selectedBook})` : ""}
|
||||||
</Text>
|
</Text>
|
||||||
<FlatList
|
<FlatList
|
||||||
data={chapterOptions}
|
data={chapterOptions}
|
||||||
@@ -220,7 +219,7 @@ const BiblePicker = ({ route }) => {
|
|||||||
{activeStep === "verse" ? (
|
{activeStep === "verse" ? (
|
||||||
<>
|
<>
|
||||||
<Text style={{ fontSize: 14, fontWeight: "600", marginBottom: 8 }}>
|
<Text style={{ fontSize: 14, fontWeight: "600", marginBottom: 8 }}>
|
||||||
Verses {selectedBook && chapter ? `(${selectedBook} ${chapter})` : ""}
|
{i18n.t("message.verses")} {selectedBook && chapter ? `(${selectedBook} ${chapter})` : ""}
|
||||||
</Text>
|
</Text>
|
||||||
{loadingVerses ? (
|
{loadingVerses ? (
|
||||||
<ActivityIndicator style={{ marginTop: 8 }} />
|
<ActivityIndicator style={{ marginTop: 8 }} />
|
||||||
|
|||||||
+1
-1
@@ -248,7 +248,7 @@ let NewPostView = (props) => {
|
|||||||
mode="outlined"
|
mode="outlined"
|
||||||
onPress={() => navigation.navigate("BiblePicker", { target: "post" })}
|
onPress={() => navigation.navigate("BiblePicker", { target: "post" })}
|
||||||
>
|
>
|
||||||
Bible
|
{i18n.t("message.bible")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button icon="add-a-photo" mode="outlined" onPress={pickImage}>
|
<Button icon="add-a-photo" mode="outlined" onPress={pickImage}>
|
||||||
{i18n.t("message.addPhotos")}
|
{i18n.t("message.addPhotos")}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { StyleSheet, Text, View } from "react-native";
|
|||||||
import { ActivityIndicator, Chip } from "react-native-paper";
|
import { ActivityIndicator, Chip } from "react-native-paper";
|
||||||
import { useNavigation } from "@react-navigation/native";
|
import { useNavigation } from "@react-navigation/native";
|
||||||
import { extractBibleReferences, fetchBiblePassage } from "../utils/bibleReferences.js";
|
import { extractBibleReferences, fetchBiblePassage } from "../utils/bibleReferences.js";
|
||||||
|
import i18n from "../i18nMessages.js";
|
||||||
|
|
||||||
const BibleEmbeddedView = ({ content = "", compact = false, openChapterOnPress = false }) => {
|
const BibleEmbeddedView = ({ content = "", compact = false, openChapterOnPress = false }) => {
|
||||||
const navigation = useNavigation();
|
const navigation = useNavigation();
|
||||||
@@ -23,7 +24,7 @@ const BibleEmbeddedView = ({ content = "", compact = false, openChapterOnPress =
|
|||||||
|
|
||||||
setByReference((prev) => ({ ...prev, [reference]: { loading: true } }));
|
setByReference((prev) => ({ ...prev, [reference]: { loading: true } }));
|
||||||
try {
|
try {
|
||||||
const data = await fetchBiblePassage(reference);
|
const data = await fetchBiblePassage(reference, i18n.locale);
|
||||||
setByReference((prev) => ({ ...prev, [reference]: { loading: false, ...data } }));
|
setByReference((prev) => ({ ...prev, [reference]: { loading: false, ...data } }));
|
||||||
} catch (_error) {
|
} catch (_error) {
|
||||||
setByReference((prev) => ({ ...prev, [reference]: { loading: false, error: true } }));
|
setByReference((prev) => ({ ...prev, [reference]: { loading: false, error: true } }));
|
||||||
@@ -34,7 +35,7 @@ const BibleEmbeddedView = ({ content = "", compact = false, openChapterOnPress =
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.container, compact ? styles.compactContainer : null]}>
|
<View style={[styles.container, compact ? styles.compactContainer : null]}>
|
||||||
<Text style={styles.label}>Bible</Text>
|
<Text style={styles.label}>{i18n.t("message.bible")}</Text>
|
||||||
<View style={styles.chipsWrap}>
|
<View style={styles.chipsWrap}>
|
||||||
{references.map((reference) => (
|
{references.map((reference) => (
|
||||||
<Chip
|
<Chip
|
||||||
@@ -58,7 +59,7 @@ const BibleEmbeddedView = ({ content = "", compact = false, openChapterOnPress =
|
|||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
) : null}
|
) : null}
|
||||||
{selectedData?.error ? <Text style={styles.errorText}>Unable to load this passage.</Text> : null}
|
{selectedData?.error ? <Text style={styles.errorText}>{i18n.t("message.unableLoadPassage")}</Text> : null}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -128,6 +128,23 @@ const messages = {
|
|||||||
completeProfileBody: "Add a profile photo and short description so people can recognize you.",
|
completeProfileBody: "Add a profile photo and short description so people can recognize you.",
|
||||||
completeProfileMissingHint: "Looks like your profile is still missing some details.",
|
completeProfileMissingHint: "Looks like your profile is still missing some details.",
|
||||||
completeProfileTestingHint: "This reminder is temporarily shown to everyone for testing.",
|
completeProfileTestingHint: "This reminder is temporarily shown to everyone for testing.",
|
||||||
|
bible: "Bible",
|
||||||
|
bibleReferenceTitle: "Bible Reference",
|
||||||
|
biblePickerSubtitlePost: "Pick a reference to insert into your post.",
|
||||||
|
biblePickerSubtitleChat: "Pick a reference to insert into your chat message.",
|
||||||
|
preview: "Preview",
|
||||||
|
useReference: "Use Reference",
|
||||||
|
selected: "Selected",
|
||||||
|
tapPreviewPickVerse: "Tap preview to pick verse from chapter",
|
||||||
|
tapVerseToSelect: "Tap a verse to select it.",
|
||||||
|
unableLoadPassage: "Unable to load this Bible passage.",
|
||||||
|
unableLoadChapter: "Unable to load chapter.",
|
||||||
|
book: "Book",
|
||||||
|
chapter: "Chapter",
|
||||||
|
verse: "Verse",
|
||||||
|
books: "Books",
|
||||||
|
chapters: "Chapters",
|
||||||
|
verses: "Verses",
|
||||||
updateProfile: "Update Profile",
|
updateProfile: "Update Profile",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -252,6 +269,23 @@ const messages = {
|
|||||||
completeProfileBody: "Agrega una foto de perfil y una breve descripción para que las personas puedan reconocerte.",
|
completeProfileBody: "Agrega una foto de perfil y una breve descripción para que las personas puedan reconocerte.",
|
||||||
completeProfileMissingHint: "Parece que a tu perfil todavía le faltan algunos detalles.",
|
completeProfileMissingHint: "Parece que a tu perfil todavía le faltan algunos detalles.",
|
||||||
completeProfileTestingHint: "Este recordatorio se muestra temporalmente a todos para pruebas.",
|
completeProfileTestingHint: "Este recordatorio se muestra temporalmente a todos para pruebas.",
|
||||||
|
bible: "Biblia",
|
||||||
|
bibleReferenceTitle: "Referencia bíblica",
|
||||||
|
biblePickerSubtitlePost: "Elige una referencia para insertarla en tu publicación.",
|
||||||
|
biblePickerSubtitleChat: "Elige una referencia para insertarla en tu mensaje de chat.",
|
||||||
|
preview: "Vista previa",
|
||||||
|
useReference: "Usar referencia",
|
||||||
|
selected: "Seleccionado",
|
||||||
|
tapPreviewPickVerse: "Toca la vista previa para elegir un versículo del capítulo",
|
||||||
|
tapVerseToSelect: "Toca un versículo para seleccionarlo.",
|
||||||
|
unableLoadPassage: "No se pudo cargar este pasaje bíblico.",
|
||||||
|
unableLoadChapter: "No se pudo cargar el capítulo.",
|
||||||
|
book: "Libro",
|
||||||
|
chapter: "Capítulo",
|
||||||
|
verse: "Versículo",
|
||||||
|
books: "Libros",
|
||||||
|
chapters: "Capítulos",
|
||||||
|
verses: "Versículos",
|
||||||
updateProfile: "Actualizar perfil",
|
updateProfile: "Actualizar perfil",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -376,6 +410,23 @@ const messages = {
|
|||||||
completeProfileBody: "Ajoutez une photo de profil et une courte description pour que les autres puissent vous reconnaître.",
|
completeProfileBody: "Ajoutez une photo de profil et une courte description pour que les autres puissent vous reconnaître.",
|
||||||
completeProfileMissingHint: "Il semble que votre profil manque encore de quelques informations.",
|
completeProfileMissingHint: "Il semble que votre profil manque encore de quelques informations.",
|
||||||
completeProfileTestingHint: "Ce rappel est temporairement affiché à tout le monde pour les tests.",
|
completeProfileTestingHint: "Ce rappel est temporairement affiché à tout le monde pour les tests.",
|
||||||
|
bible: "Bible",
|
||||||
|
bibleReferenceTitle: "Référence biblique",
|
||||||
|
biblePickerSubtitlePost: "Choisissez une référence à insérer dans votre publication.",
|
||||||
|
biblePickerSubtitleChat: "Choisissez une référence à insérer dans votre message de chat.",
|
||||||
|
preview: "Aperçu",
|
||||||
|
useReference: "Utiliser la référence",
|
||||||
|
selected: "Sélectionné",
|
||||||
|
tapPreviewPickVerse: "Touchez l'aperçu pour choisir un verset du chapitre",
|
||||||
|
tapVerseToSelect: "Touchez un verset pour le sélectionner.",
|
||||||
|
unableLoadPassage: "Impossible de charger ce passage biblique.",
|
||||||
|
unableLoadChapter: "Impossible de charger le chapitre.",
|
||||||
|
book: "Livre",
|
||||||
|
chapter: "Chapitre",
|
||||||
|
verse: "Verset",
|
||||||
|
books: "Livres",
|
||||||
|
chapters: "Chapitres",
|
||||||
|
verses: "Versets",
|
||||||
updateProfile: "Mettre à jour le profil",
|
updateProfile: "Mettre à jour le profil",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -500,6 +551,23 @@ const messages = {
|
|||||||
completeProfileBody: "Tilføj et profilbillede og en kort beskrivelse, så andre kan genkende dig.",
|
completeProfileBody: "Tilføj et profilbillede og en kort beskrivelse, så andre kan genkende dig.",
|
||||||
completeProfileMissingHint: "Det ser ud til, at din profil stadig mangler nogle detaljer.",
|
completeProfileMissingHint: "Det ser ud til, at din profil stadig mangler nogle detaljer.",
|
||||||
completeProfileTestingHint: "Denne påmindelse vises midlertidigt til alle for test.",
|
completeProfileTestingHint: "Denne påmindelse vises midlertidigt til alle for test.",
|
||||||
|
bible: "Bibelen",
|
||||||
|
bibleReferenceTitle: "Bibelreference",
|
||||||
|
biblePickerSubtitlePost: "Vælg en reference til at indsætte i dit opslag.",
|
||||||
|
biblePickerSubtitleChat: "Vælg en reference til at indsætte i din chatbesked.",
|
||||||
|
preview: "Forhåndsvis",
|
||||||
|
useReference: "Brug reference",
|
||||||
|
selected: "Valgt",
|
||||||
|
tapPreviewPickVerse: "Tryk på forhåndsvisning for at vælge et vers fra kapitlet",
|
||||||
|
tapVerseToSelect: "Tryk på et vers for at vælge det.",
|
||||||
|
unableLoadPassage: "Kunne ikke indlæse dette bibelafsnit.",
|
||||||
|
unableLoadChapter: "Kunne ikke indlæse kapitlet.",
|
||||||
|
book: "Bog",
|
||||||
|
chapter: "Kapitel",
|
||||||
|
verse: "Vers",
|
||||||
|
books: "Bøger",
|
||||||
|
chapters: "Kapitler",
|
||||||
|
verses: "Vers",
|
||||||
updateProfile: "Opdater profil",
|
updateProfile: "Opdater profil",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,21 @@
|
|||||||
const BIBLE_TOKEN_REGEX = /@bible:([^\s]+)/gi;
|
const BIBLE_TOKEN_REGEX = /@bible:([^\s]+)/gi;
|
||||||
|
const DEFAULT_TRANSLATION = "web";
|
||||||
|
|
||||||
|
const getNormalizedLocale = (locale = "") => {
|
||||||
|
return String(locale || "").toLowerCase().replace("_", "-").trim();
|
||||||
|
};
|
||||||
|
|
||||||
|
const getTranslationForLocale = (locale = "") => {
|
||||||
|
const normalized = getNormalizedLocale(locale);
|
||||||
|
if (!normalized) return "kjv";
|
||||||
|
if (normalized.startsWith("en-gb")) return "webbe";
|
||||||
|
if (normalized.startsWith("en")) return "kjv";
|
||||||
|
if (normalized.startsWith("zh")) return "cuv";
|
||||||
|
if (normalized.startsWith("cs")) return "bkr";
|
||||||
|
if (normalized.startsWith("pt")) return "almeida";
|
||||||
|
if (normalized.startsWith("ro")) return "rccv";
|
||||||
|
return DEFAULT_TRANSLATION;
|
||||||
|
};
|
||||||
|
|
||||||
const normalizeReference = (value = "") => {
|
const normalizeReference = (value = "") => {
|
||||||
try {
|
try {
|
||||||
@@ -45,16 +62,29 @@ export const stripBibleTokens = (content = "") => {
|
|||||||
.trim();
|
.trim();
|
||||||
};
|
};
|
||||||
|
|
||||||
export const fetchBiblePassage = async (reference = "") => {
|
export const fetchBibleReference = async (reference = "", locale = "") => {
|
||||||
|
const safeReference = normalizeReference(reference);
|
||||||
|
const preferredTranslation = getTranslationForLocale(locale);
|
||||||
|
const preferredUrl = `https://bible-api.com/${encodeURIComponent(safeReference)}?translation=${preferredTranslation}`;
|
||||||
|
const fallbackUrl = `https://bible-api.com/${encodeURIComponent(safeReference)}?translation=${DEFAULT_TRANSLATION}`;
|
||||||
|
|
||||||
|
let response = await fetch(preferredUrl);
|
||||||
|
if (!response.ok && preferredTranslation !== DEFAULT_TRANSLATION) {
|
||||||
|
response = await fetch(fallbackUrl);
|
||||||
|
}
|
||||||
|
if (!response.ok) throw new Error("Failed to load Bible passage");
|
||||||
|
|
||||||
|
const payload = await response.json();
|
||||||
|
if (payload?.error) throw new Error(payload.error);
|
||||||
|
return payload;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fetchBiblePassage = async (reference = "", locale = "") => {
|
||||||
const safeReference = normalizeReference(reference);
|
const safeReference = normalizeReference(reference);
|
||||||
if (!safeReference) {
|
if (!safeReference) {
|
||||||
throw new Error("Missing Bible reference");
|
throw new Error("Missing Bible reference");
|
||||||
}
|
}
|
||||||
const response = await fetch(`https://bible-api.com/${encodeURIComponent(safeReference)}`);
|
const payload = await fetchBibleReference(safeReference, locale);
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error("Failed to load Bible passage");
|
|
||||||
}
|
|
||||||
const payload = await response.json();
|
|
||||||
return {
|
return {
|
||||||
reference: payload?.reference || safeReference,
|
reference: payload?.reference || safeReference,
|
||||||
text: (payload?.text || "").trim(),
|
text: (payload?.text || "").trim(),
|
||||||
@@ -62,6 +92,11 @@ export const fetchBiblePassage = async (reference = "") => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const fetchBibleChapter = async (chapterReference = "", locale = "") => {
|
||||||
|
const safeReference = normalizeReference(chapterReference);
|
||||||
|
return fetchBibleReference(safeReference, locale);
|
||||||
|
};
|
||||||
|
|
||||||
export const parseBibleReference = (reference = "") => {
|
export const parseBibleReference = (reference = "") => {
|
||||||
const normalized = normalizeReference(reference);
|
const normalized = normalizeReference(reference);
|
||||||
const match = normalized.match(/^(.*)\s+(\d+)(?::(\d+)(?:-\d+)?)?$/);
|
const match = normalized.match(/^(.*)\s+(\d+)(?::(\d+)(?:-\d+)?)?$/);
|
||||||
|
|||||||
Reference in New Issue
Block a user