diff --git a/apps/mobile/src/components/ui/image/ImageContextMenu.tsx b/apps/mobile/src/components/ui/image/ImageContextMenu.tsx
index ecf6f6b0e..4e161b018 100644
--- a/apps/mobile/src/components/ui/image/ImageContextMenu.tsx
+++ b/apps/mobile/src/components/ui/image/ImageContextMenu.tsx
@@ -6,6 +6,7 @@ import { saveToLibraryAsync } from "expo-media-library"
import { shareAsync } from "expo-sharing"
import type { PropsWithChildren } from "react"
import { useRef } from "react"
+import { useTranslation } from "react-i18next"
import type { View } from "react-native"
import { findNodeHandle, Image, Pressable } from "react-native"
@@ -35,6 +36,7 @@ const getIOSNativeImageActions = () => {
}
export const ImageContextMenu = ({ imageUrl, entryId, children }: ImageContextMenuProps) => {
+ const { t } = useTranslation()
const entry = useEntry(entryId!)
const feedId = entry?.feedId
const view = useSelectedView()
@@ -105,7 +107,9 @@ export const ImageContextMenu = ({ imageUrl, entryId, children }: ImageContextMe
name: isEntryStarred ? "star.slash" : "star",
}}
/>
- {isEntryStarred ? "Unstar" : "Star"}
+
+ {isEntryStarred ? t("operation.unstar") : t("operation.star")}
+
)}
@@ -183,7 +187,7 @@ export const ImageContextMenu = ({ imageUrl, entryId, children }: ImageContextMe
name: "square.and.arrow.up",
}}
/>
- Share
+ {t("operation.share")}
diff --git a/apps/mobile/src/lib/dialog.tsx b/apps/mobile/src/lib/dialog.tsx
index 924e29e47..02c94c504 100644
--- a/apps/mobile/src/lib/dialog.tsx
+++ b/apps/mobile/src/lib/dialog.tsx
@@ -1,4 +1,5 @@
import { cn } from "@follow/utils"
+import { t } from "i18next"
import type { Dispatch, FC, ReactElement, ReactNode, SetStateAction } from "react"
import {
cloneElement,
@@ -213,14 +214,14 @@ class DialogStatic {
) => {
+ const { t } = useTranslation()
const entry = useEntry(id)
const feedId = entry?.feedId
const view = useSelectedView()
@@ -62,7 +64,7 @@ export const EntryItemContextMenu = ({ id, children }: PropsWithChildren<{ id: s
unreadSyncService.markEntryAsRead(id)
}}
>
- Mark as Read
+ {t("operation.mark_as_read")}
- {isEntryStarred ? "Unstar" : "Star"}
+
+ {isEntryStarred ? t("operation.unstar") : t("operation.star")}
+
)}
@@ -109,7 +113,7 @@ export const EntryItemContextMenu = ({ id, children }: PropsWithChildren<{ id: s
name: "link",
}}
/>
- Open Link
+ {t("operation.open_link")}
)}
@@ -130,7 +134,7 @@ export const EntryItemContextMenu = ({ id, children }: PropsWithChildren<{ id: s
name: "square.and.arrow.up",
}}
/>
- Share
+ {t("operation.share")}
)}
diff --git a/apps/mobile/src/modules/context-menu/feeds.tsx b/apps/mobile/src/modules/context-menu/feeds.tsx
index 6efe0a861..2e2de4b4f 100644
--- a/apps/mobile/src/modules/context-menu/feeds.tsx
+++ b/apps/mobile/src/modules/context-menu/feeds.tsx
@@ -1,6 +1,7 @@
import { FeedViewType } from "@follow/constants"
import type { FC, PropsWithChildren } from "react"
import { useCallback, useMemo } from "react"
+import { useTranslation } from "react-i18next"
import type { ListRenderItemInfo } from "react-native"
import { Alert, Clipboard, FlatList, View } from "react-native"
@@ -25,6 +26,7 @@ export const SubscriptionFeedItemContextMenu: FC<
view?: FeedViewType
}
> = ({ id, children, view }) => {
+ const { t } = useTranslation()
const allCategories = useSubscriptionCategory(view)
const navigation = useNavigation()
return (
@@ -50,7 +52,7 @@ export const SubscriptionFeedItemContextMenu: FC<
unreadSyncService.markFeedAsRead(id)
}, [id])}
>
- Mark All As Read
+ {t("operation.mark_all_as_read")}
- Add To Category
+ {t("operation.add_feeds_to_category")}
@@ -126,7 +128,7 @@ export const SubscriptionFeedItemContextMenu: FC<
- Edit
+ {t("operation.edit")}
- Copy Link
+ {t("operation.copy_link")}
{
subscriptionSyncService.unsubscribe(id)
},
},
])
- }, [id])}
+ }, [id, t])}
>
- Unsubscribe
+ {t("operation.unfollow")}
) => {
+ const { t } = useTranslation()
return (
{children}
@@ -213,7 +216,7 @@ export const SubscriptionFeedCategoryContextMenu = ({
unreadSyncService.markFeedAsRead(feedIds)
}, [feedIds])}
>
- Mark All As Read
+ {t("operation.mark_all_as_read")}
- Change To Other View
+ {t("operation.change_to_other_view")}
@@ -243,7 +246,7 @@ export const SubscriptionFeedCategoryContextMenu = ({
- Edit Category
+ {t("operation.rename_category")}
- Delete Category
+ {t("operation.delete_category")}
= ({ id, children }) => {
+ const { t } = useTranslation()
const isOwnList = useIsOwnList(id)
const actions = useMemo(
() => [
isOwnList && {
- title: "Edit",
+ title: t("operation.edit"),
onSelect: () => {
const list = getList(id)
if (!list) return
@@ -26,7 +28,7 @@ export const SubscriptionListItemContextMenu: FC<
},
},
{
- title: "Copy Link",
+ title: t("operation.copy_link"),
onSelect: () => {
const list = getList(id)
if (!list) return
@@ -35,16 +37,16 @@ export const SubscriptionListItemContextMenu: FC<
},
},
{
- title: "Unsubscribe",
+ title: t("operation.unfollow"),
destructive: true,
onSelect: () => {
- Alert.alert("Unsubscribe", "Are you sure you want to unsubscribe?", [
+ Alert.alert(t("operation.unfollow"), "Are you sure you want to unsubscribe?", [
{
text: "Cancel",
style: "cancel",
},
{
- text: "Unsubscribe",
+ text: t("operation.unfollow"),
style: "destructive",
onPress: () => {
subscriptionSyncService.unsubscribe(id)
@@ -54,7 +56,7 @@ export const SubscriptionListItemContextMenu: FC<
},
},
],
- [id, isOwnList],
+ [id, isOwnList, t],
)
return (
diff --git a/apps/mobile/src/modules/context-menu/video.tsx b/apps/mobile/src/modules/context-menu/video.tsx
index c34ea2ea7..917ef0ffd 100644
--- a/apps/mobile/src/modules/context-menu/video.tsx
+++ b/apps/mobile/src/modules/context-menu/video.tsx
@@ -1,4 +1,5 @@
import type { PropsWithChildren } from "react"
+import { useTranslation } from "react-i18next"
import { Clipboard, Share } from "react-native"
import { ContextMenu } from "@/src/components/ui/context-menu"
@@ -14,6 +15,7 @@ type VideoContextMenuProps = PropsWithChildren<{
}>
export const VideoContextMenu = ({ entryId, children }: VideoContextMenuProps) => {
+ const { t } = useTranslation()
const entry = useEntry(entryId)
const feedId = entry?.feedId
const view = useSelectedView()
@@ -50,7 +52,9 @@ export const VideoContextMenu = ({ entryId, children }: VideoContextMenuProps) =
name: isEntryStarred ? "star.slash" : "star",
}}
/>
- {isEntryStarred ? "Unstar" : "Star"}
+
+ {isEntryStarred ? t("operation.unstar") : t("operation.star")}
+
)}
@@ -66,7 +70,7 @@ export const VideoContextMenu = ({ entryId, children }: VideoContextMenuProps) =
name: "link",
}}
/>
- Open Link
+ {t("operation.open_link")}
- Copy Link
+ {t("operation.copy_link")}
- Share
+ {t("operation.share")}
diff --git a/apps/mobile/src/modules/dialogs/MarkAllAsReadDialog.tsx b/apps/mobile/src/modules/dialogs/MarkAllAsReadDialog.tsx
index 665be7bd1..88434a677 100644
--- a/apps/mobile/src/modules/dialogs/MarkAllAsReadDialog.tsx
+++ b/apps/mobile/src/modules/dialogs/MarkAllAsReadDialog.tsx
@@ -1,3 +1,5 @@
+import { t } from "i18next"
+import { useTranslation } from "react-i18next"
import { Text, View } from "react-native"
import { CheckCircleCuteReIcon } from "@/src/icons/check_circle_cute_re"
@@ -8,11 +10,12 @@ import { unreadSyncService } from "@/src/store/unread/store"
import { useSelectedView } from "../screen/atoms"
export const MarkAllAsReadDialog: DialogComponent = () => {
+ const { t } = useTranslation()
const selectedView = useSelectedView()
const ctx = Dialog.useDialogContext()
return (
- Do you want to mark all items as read?
+ {t("operation.mark_all_as_read_confirm")}
{
ctx?.dismiss()
@@ -26,8 +29,7 @@ export const MarkAllAsReadDialog: DialogComponent = () => {
)
}
-MarkAllAsReadDialog.title = "Mark All as Read"
+MarkAllAsReadDialog.title = t("operation.mark_all_as_read")
MarkAllAsReadDialog.id = "mark-all-as-read"
-MarkAllAsReadDialog.title = "Mark All as Read"
MarkAllAsReadDialog.headerIcon =
diff --git a/apps/mobile/src/modules/entry-content/EntryContentHeaderRightActions.tsx b/apps/mobile/src/modules/entry-content/EntryContentHeaderRightActions.tsx
index d9cdd8469..056e287b3 100644
--- a/apps/mobile/src/modules/entry-content/EntryContentHeaderRightActions.tsx
+++ b/apps/mobile/src/modules/entry-content/EntryContentHeaderRightActions.tsx
@@ -1,5 +1,6 @@
import { useAtom } from "jotai"
import { useCallback, useEffect, useState } from "react"
+import { useTranslation } from "react-i18next"
import { Clipboard, Share, TouchableOpacity, View } from "react-native"
import type { SharedValue } from "react-native-reanimated"
import Animated, { interpolate, useAnimatedStyle } from "react-native-reanimated"
@@ -56,6 +57,7 @@ const HeaderRightActionsImpl = ({
titleOpacityShareValue,
isHeaderTitleVisible,
}: HeaderRightActionsProps) => {
+ const { t } = useTranslation()
const labelColor = useColor("label")
const isStarred = useIsEntryStarred(entryId)
const [extraActionContainerWidth, setExtraActionContainerWidth] = useState(0)
@@ -150,7 +152,7 @@ const HeaderRightActionsImpl = ({
const actionItems = [
subscription && {
key: "Star",
- title: isStarred ? "Unstar" : "Star",
+ title: isStarred ? t("operation.unstar") : t("operation.star"),
icon: isStarred ? : ,
iconIOS: {
name: isStarred ? "star.fill" : "star",
@@ -189,7 +191,7 @@ const HeaderRightActionsImpl = ({
},
{
key: "Share",
- title: "Share",
+ title: t("operation.share"),
icon: ,
iconIOS: { name: "square.and.arrow.up" },
onPress: handleShare,
@@ -257,7 +259,7 @@ const HeaderRightActionsImpl = ({
)}
- Copy Link
+ {t("operation.copy_link")}
diff --git a/apps/mobile/src/modules/screen/TimelineSelectorProvider.tsx b/apps/mobile/src/modules/screen/TimelineSelectorProvider.tsx
index 81a232929..1ff949a55 100644
--- a/apps/mobile/src/modules/screen/TimelineSelectorProvider.tsx
+++ b/apps/mobile/src/modules/screen/TimelineSelectorProvider.tsx
@@ -1,5 +1,6 @@
import { env } from "@follow/shared/src/env"
import { useMemo } from "react"
+import { useTranslation } from "react-i18next"
import { Share, View } from "react-native"
import { useColor } from "react-native-uikit-colors"
@@ -76,12 +77,13 @@ export function TimelineSelectorProvider({
}
function FeedShareAction({ feedId }: { feedId?: string }) {
+ const { t } = useTranslation()
const label = useColor("label")
if (!feedId) return null
return (
}
onPress={() => {
const feed = getFeed(feedId)
diff --git a/apps/mobile/src/modules/screen/TimelineViewSelectorContextMenu.tsx b/apps/mobile/src/modules/screen/TimelineViewSelectorContextMenu.tsx
index 2c41a1e91..cf661279b 100644
--- a/apps/mobile/src/modules/screen/TimelineViewSelectorContextMenu.tsx
+++ b/apps/mobile/src/modules/screen/TimelineViewSelectorContextMenu.tsx
@@ -1,5 +1,6 @@
import type { FeedViewType } from "@follow/constants"
import type { FC, PropsWithChildren } from "react"
+import { useTranslation } from "react-i18next"
import { ContextMenu } from "@/src/components/ui/context-menu"
import { unreadSyncService } from "@/src/store/unread/store"
@@ -7,6 +8,7 @@ import { unreadSyncService } from "@/src/store/unread/store"
export const TimelineViewSelectorContextMenu: FC<
PropsWithChildren<{ type: string | undefined; viewId: FeedViewType | undefined }>
> = ({ children, type, viewId }) => {
+ const { t } = useTranslation()
if (type !== "view" || viewId === undefined) return children
return (
@@ -19,7 +21,7 @@ export const TimelineViewSelectorContextMenu: FC<
unreadSyncService.markViewAsRead(viewId)
}}
>
- Mark as Read
+ {t("operation.mark_as_read")}
{props.children}
}
onPress={() => {
Dialog.show(MarkAllAsReadDialog)
@@ -80,17 +82,27 @@ const useButtonVariant = ({ variant = "primary" }: HeaderActionButtonProps) => {
return { size, color }
}
export const UnreadOnlyActionButton = ({ variant = "primary" }: HeaderActionButtonProps) => {
+ const { t } = useTranslation()
const unreadOnly = useGeneralSettingKey("unreadOnly")
const { size, color } = useButtonVariant({ variant })
return (
}
selectedIcon={}
onPress={() => {
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light)
setGeneralSetting("unreadOnly", !unreadOnly)
- toast.success(`Showing ${unreadOnly ? "all" : "unread"} entries`, { position: "bottom" })
+ toast.success(
+ unreadOnly
+ ? t("operation.toggle_unread_only.show_all.success")
+ : t("operation.toggle_unread_only.show_unread_only.success"),
+ { position: "bottom" },
+ )
}}
selected={unreadOnly}
overlay={false}
diff --git a/locales/mobile/default/ar-DZ.json b/locales/mobile/default/ar-DZ.json
index 22ea0f019..f7045ec36 100644
--- a/locales/mobile/default/ar-DZ.json
+++ b/locales/mobile/default/ar-DZ.json
@@ -1,4 +1,16 @@
{
+ "operation.change_to_other_view": "التغيير إلى عرض آخر",
+ "operation.copy_link": "نسخ الرابط",
+ "operation.delete_category": "حذف الفئة",
+ "operation.edit": "تحرير",
+ "operation.mark_all_as_read": "تحديد الكل كمقروء",
+ "operation.mark_all_as_read_confirm": "تأكيد تحديد الكل كمقروء؟",
+ "operation.mark_all_as_read_which": "هل تريد تحديد كمقروء؟",
+ "operation.mark_as_read": "تحديد كمقروء",
+ "operation.rename_category": "إعادة تسمية الفئة",
+ "operation.share": "مشاركة",
+ "operation.star": "تمييز بنجمة",
+ "operation.unfollow": "إلغاء المتابعة",
"profile.title": "ملف {{name}} الشخصي",
"profile.uncategorized_feeds": "الخلاصات غير المصنفة",
"tabs.discover": "اكتشف",
diff --git a/locales/mobile/default/ar-IQ.json b/locales/mobile/default/ar-IQ.json
index 22ea0f019..fb6eaa1bf 100644
--- a/locales/mobile/default/ar-IQ.json
+++ b/locales/mobile/default/ar-IQ.json
@@ -1,4 +1,16 @@
{
+ "operation.change_to_other_view": "تغيير إلى عرض آخر",
+ "operation.copy_link": "نسخ الرابط",
+ "operation.delete_category": "حذف الفئة",
+ "operation.edit": "تحرير",
+ "operation.mark_all_as_read": "تحديد الكل كمقروء",
+ "operation.mark_all_as_read_confirm": "تأكيد تحديد الكل كمقروء؟",
+ "operation.mark_all_as_read_which": "تحديد كمقروء؟",
+ "operation.mark_as_read": "تحديد كمقروء",
+ "operation.rename_category": "إعادة تسمية الفئة",
+ "operation.share": "مشاركة",
+ "operation.star": "إضافة إلى المفضلة",
+ "operation.unfollow": "إلغاء المتابعة",
"profile.title": "ملف {{name}} الشخصي",
"profile.uncategorized_feeds": "الخلاصات غير المصنفة",
"tabs.discover": "اكتشف",
diff --git a/locales/mobile/default/ar-KW.json b/locales/mobile/default/ar-KW.json
index 22ea0f019..5bca7addb 100644
--- a/locales/mobile/default/ar-KW.json
+++ b/locales/mobile/default/ar-KW.json
@@ -1,4 +1,16 @@
{
+ "operation.change_to_other_view": "التبديل إلى طريقة عرض أخرى",
+ "operation.copy_link": "نسخ الرابط",
+ "operation.delete_category": "حذف الفئة",
+ "operation.edit": "تحرير",
+ "operation.mark_all_as_read": "وضع علامة الكل كمقروء",
+ "operation.mark_all_as_read_confirm": "تأكيد وضع علامة الجميع كمقروء؟",
+ "operation.mark_all_as_read_which": "وضع علامة كمقروء؟",
+ "operation.mark_as_read": "وضع علامة كمقروء",
+ "operation.rename_category": "إعادة تسمية الفئة",
+ "operation.share": "مشاركة",
+ "operation.star": "تفضيل",
+ "operation.unfollow": "إلغاء المتابعة",
"profile.title": "ملف {{name}} الشخصي",
"profile.uncategorized_feeds": "الخلاصات غير المصنفة",
"tabs.discover": "اكتشف",
diff --git a/locales/mobile/default/ar-MA.json b/locales/mobile/default/ar-MA.json
index 22ea0f019..0e009073c 100644
--- a/locales/mobile/default/ar-MA.json
+++ b/locales/mobile/default/ar-MA.json
@@ -1,4 +1,16 @@
{
+ "operation.change_to_other_view": "تغيير إلى عرض آخر",
+ "operation.copy_link": "نسخ الرابط",
+ "operation.delete_category": "حذف الفئة",
+ "operation.edit": "تعديل",
+ "operation.mark_all_as_read": "وضع علامة على الكل كمقروء",
+ "operation.mark_all_as_read_confirm": "تأكيد وضع علامة على الكل كمقروء؟",
+ "operation.mark_all_as_read_which": "وضع علامة على كمقروء؟",
+ "operation.mark_as_read": "وضع علامة كمقروء",
+ "operation.rename_category": "إعادة تسمية الفئة",
+ "operation.share": "مشاركة",
+ "operation.star": "إضافة إلى المفضلة",
+ "operation.unfollow": "إلغاء المتابعة",
"profile.title": "ملف {{name}} الشخصي",
"profile.uncategorized_feeds": "الخلاصات غير المصنفة",
"tabs.discover": "اكتشف",
diff --git a/locales/mobile/default/ar-SA.json b/locales/mobile/default/ar-SA.json
index 22ea0f019..e99c2c663 100644
--- a/locales/mobile/default/ar-SA.json
+++ b/locales/mobile/default/ar-SA.json
@@ -1,4 +1,16 @@
{
+ "operation.change_to_other_view": "التغيير إلى عرض آخر",
+ "operation.copy_link": "نسخ الرابط",
+ "operation.delete_category": "حذف الفئة",
+ "operation.edit": "تحرير",
+ "operation.mark_all_as_read": "تمييز الكل كمقروء",
+ "operation.mark_all_as_read_confirm": "هل ترغب في تأكيد تمييز الكل كمقروء؟",
+ "operation.mark_all_as_read_which": "تمييز كمقروء؟",
+ "operation.mark_as_read": "تمييز كمقروء",
+ "operation.rename_category": "إعادة تسمية الفئة",
+ "operation.share": "مشاركة",
+ "operation.star": "تمييز",
+ "operation.unfollow": "إلغاء المتابعة",
"profile.title": "ملف {{name}} الشخصي",
"profile.uncategorized_feeds": "الخلاصات غير المصنفة",
"tabs.discover": "اكتشف",
diff --git a/locales/mobile/default/ar-TN.json b/locales/mobile/default/ar-TN.json
index 22ea0f019..a886834cd 100644
--- a/locales/mobile/default/ar-TN.json
+++ b/locales/mobile/default/ar-TN.json
@@ -1,4 +1,16 @@
{
+ "operation.change_to_other_view": "التغيير إلى عرض آخر",
+ "operation.copy_link": "نسخ الرابط",
+ "operation.delete_category": "حذف الفئة",
+ "operation.edit": "تحرير",
+ "operation.mark_all_as_read": "وضع علامة على الكل كمقروء",
+ "operation.mark_all_as_read_confirm": "تأكيد وضع الجميع كمقروء؟",
+ "operation.mark_all_as_read_which": "وضع علامة كمقروء؟",
+ "operation.mark_as_read": "وضع علامة كمقروء",
+ "operation.rename_category": "إعادة تسمية الفئة",
+ "operation.share": "مشاركة",
+ "operation.star": "إضافة إلى المفضلة",
+ "operation.unfollow": "إلغاء المتابعة",
"profile.title": "ملف {{name}} الشخصي",
"profile.uncategorized_feeds": "الخلاصات غير المصنفة",
"tabs.discover": "اكتشف",
diff --git a/locales/mobile/default/de.json b/locales/mobile/default/de.json
index bbf2b37c7..d95e1e61a 100644
--- a/locales/mobile/default/de.json
+++ b/locales/mobile/default/de.json
@@ -1,4 +1,16 @@
{
+ "operation.change_to_other_view": "Zu anderer Ansicht wechseln",
+ "operation.copy_link": "Link kopieren",
+ "operation.delete_category": "Kategorie löschen",
+ "operation.edit": "Bearbeiten",
+ "operation.mark_all_as_read": "Alles als gelesen markieren",
+ "operation.mark_all_as_read_confirm": "Möchten Sie alle als gelesen markieren?",
+ "operation.mark_all_as_read_which": " als gelesen markieren?",
+ "operation.mark_as_read": "Als gelesen markieren",
+ "operation.rename_category": "Kategorie umbenennen",
+ "operation.share": "Teilen",
+ "operation.star": "Favorisieren",
+ "operation.unfollow": "Entfolgen",
"profile.title": "{{name}}s Profil",
"profile.uncategorized_feeds": "Unkategorisierte Feeds",
"tabs.discover": "Entdecken",
diff --git a/locales/mobile/default/en.json b/locales/mobile/default/en.json
index 33fcd2224..55c294913 100644
--- a/locales/mobile/default/en.json
+++ b/locales/mobile/default/en.json
@@ -1,4 +1,23 @@
{
+ "operation.add_feeds_to_category": "Move to Category",
+ "operation.change_to_other_view": "Switch to Another View",
+ "operation.copy_link": "Copy Link",
+ "operation.delete_category": "Delete Category",
+ "operation.edit": "Edit",
+ "operation.mark_all_as_read": "Mark all as read",
+ "operation.mark_all_as_read_confirm": "Confirm mark all as read?",
+ "operation.mark_all_as_read_which": "Mark as read?",
+ "operation.mark_as_read": "Mark as Read",
+ "operation.open_link": "Open Link",
+ "operation.rename_category": "Rename Category",
+ "operation.share": "Share",
+ "operation.star": "Star",
+ "operation.toggle_unread_only.show_all.label": "Show All",
+ "operation.toggle_unread_only.show_all.success": "Showing all entries",
+ "operation.toggle_unread_only.show_unread_only.label": "Show Unread Only",
+ "operation.toggle_unread_only.show_unread_only.success": "Showing unread entries only",
+ "operation.unfollow": "Unfollow",
+ "operation.unstar": "Unstar",
"profile.title": "{{name}}'s Profile",
"profile.uncategorized_feeds": "Uncategorized feeds",
"tabs.discover": "Discover",
diff --git a/locales/mobile/default/es.json b/locales/mobile/default/es.json
index 017f6f1c5..1d1fc4d4b 100644
--- a/locales/mobile/default/es.json
+++ b/locales/mobile/default/es.json
@@ -1,4 +1,16 @@
{
+ "operation.change_to_other_view": "Cambiar a otra vista",
+ "operation.copy_link": "Copiar enlace",
+ "operation.delete_category": "Eliminar categoría",
+ "operation.edit": "Editar",
+ "operation.mark_all_as_read": "Marcar todo como leído",
+ "operation.mark_all_as_read_confirm": "¿Confirmar marcar todo como leído?",
+ "operation.mark_all_as_read_which": "¿Marcar como leído?",
+ "operation.mark_as_read": "Marcar como leído",
+ "operation.rename_category": "Renombrar categoría",
+ "operation.share": "Compartir",
+ "operation.star": "Agregar a favoritos",
+ "operation.unfollow": "Dejar de seguir",
"profile.title": "Perfil de {{name}}",
"profile.uncategorized_feeds": "Feeds sin categorizar",
"tabs.discover": "Descubrir",
diff --git a/locales/mobile/default/fi.json b/locales/mobile/default/fi.json
index f823fa58e..e8f6e8675 100644
--- a/locales/mobile/default/fi.json
+++ b/locales/mobile/default/fi.json
@@ -1,4 +1,16 @@
{
+ "operation.change_to_other_view": "Vaihda toiseen näkymään",
+ "operation.copy_link": "Kopioi linkki",
+ "operation.delete_category": "Poista kategoria",
+ "operation.edit": "Muokkaa",
+ "operation.mark_all_as_read": "Merkitse kaikki luetuiksi",
+ "operation.mark_all_as_read_confirm": "Vahvista kaikki merkinnät luetuiksi?",
+ "operation.mark_all_as_read_which": "Merkitäänkö luetuksi?",
+ "operation.mark_as_read": "Merkitse luetuksi",
+ "operation.rename_category": "Nimeä kategoria uudelleen",
+ "operation.share": "Jaa",
+ "operation.star": "Tähtää",
+ "operation.unfollow": "Lopeta seuraaminen",
"profile.title": "{{name}}n profiili",
"profile.uncategorized_feeds": "Luokittelemattomat syötteet",
"tabs.discover": "Löydä",
diff --git a/locales/mobile/default/fr.json b/locales/mobile/default/fr.json
index c405eec37..c8fc54262 100644
--- a/locales/mobile/default/fr.json
+++ b/locales/mobile/default/fr.json
@@ -1,4 +1,16 @@
{
+ "operation.change_to_other_view": "Changer pour une autre vue",
+ "operation.copy_link": "Copier le lien",
+ "operation.delete_category": "Supprimer la catégorie",
+ "operation.edit": "Éditer",
+ "operation.mark_all_as_read": "Tout marquer comme lu",
+ "operation.mark_all_as_read_confirm": "Confirmez que vous voulez tout marquer comme lu ?",
+ "operation.mark_all_as_read_which": "Marquer comme lu ?",
+ "operation.mark_as_read": "Marquer comme lu",
+ "operation.rename_category": "Renommer la catégorie",
+ "operation.share": "Partager",
+ "operation.star": "Mettre en favori",
+ "operation.unfollow": "Se désabonner",
"profile.title": "Profil de {{name}}",
"profile.uncategorized_feeds": "Flux non catégorisés",
"tabs.discover": "Découvrir",
diff --git a/locales/mobile/default/it.json b/locales/mobile/default/it.json
index 580fd07fc..f376d7633 100644
--- a/locales/mobile/default/it.json
+++ b/locales/mobile/default/it.json
@@ -1,4 +1,16 @@
{
+ "operation.change_to_other_view": "Cambia ad altra visualizzazione",
+ "operation.copy_link": "Copia link",
+ "operation.delete_category": "Elimina categoria",
+ "operation.edit": "Modifica",
+ "operation.mark_all_as_read": "Segna tutto come letto",
+ "operation.mark_all_as_read_confirm": "Confermi di segnare tutto come letto?",
+ "operation.mark_all_as_read_which": "Segna come letto?",
+ "operation.mark_as_read": "Segna come letto",
+ "operation.rename_category": "Rinomina categoria",
+ "operation.share": "Condividi",
+ "operation.star": "Aggiungi ai preferiti",
+ "operation.unfollow": "Smetti di seguire",
"profile.title": "Profilo di {{name}}",
"profile.uncategorized_feeds": "Feed non categorizzati",
"tabs.discover": "Scopri",
diff --git a/locales/mobile/default/ja.json b/locales/mobile/default/ja.json
index 0a0b31ad3..7eba4e24e 100644
--- a/locales/mobile/default/ja.json
+++ b/locales/mobile/default/ja.json
@@ -1,4 +1,17 @@
{
+ "operation.add_feeds_to_category": "カテゴリーに移動",
+ "operation.change_to_other_view": "他のビューに切り替え",
+ "operation.copy_link": "リンクをコピー",
+ "operation.delete_category": "カテゴリーを削除",
+ "operation.edit": "編集",
+ "operation.mark_all_as_read": "すべてを既読にする",
+ "operation.mark_all_as_read_confirm": "すべてを既読にしますか?",
+ "operation.mark_all_as_read_which": "を既読にしますか?",
+ "operation.mark_as_read": "既読にする",
+ "operation.rename_category": "カテゴリーをリネーム",
+ "operation.share": "共有",
+ "operation.star": "スター",
+ "operation.unfollow": "フォロー解除",
"profile.title": "{{name}}のプロフィール",
"profile.uncategorized_feeds": "未分類のフィード",
"tabs.discover": "発見",
diff --git a/locales/mobile/default/ko.json b/locales/mobile/default/ko.json
index 1df463bf0..13cb5da5e 100644
--- a/locales/mobile/default/ko.json
+++ b/locales/mobile/default/ko.json
@@ -1,4 +1,17 @@
{
+ "operation.add_feeds_to_category": "카테고리로 이동",
+ "operation.change_to_other_view": "다른 보기로 변경",
+ "operation.copy_link": "링크 복사",
+ "operation.delete_category": "카테고리 삭제",
+ "operation.edit": "편집",
+ "operation.mark_all_as_read": "모두 읽음으로 표시",
+ "operation.mark_all_as_read_confirm": "모두 읽음으로 표시하시겠습니까?",
+ "operation.mark_all_as_read_which": "을 (를) 모두 읽음으로 표시하시겠습니까?",
+ "operation.mark_as_read": "읽음으로 표시",
+ "operation.rename_category": "카테고리 이름 변경",
+ "operation.share": "공유",
+ "operation.star": "별표",
+ "operation.unfollow": "언팔로우",
"profile.title": "{{name}}의 프로필",
"profile.uncategorized_feeds": "분류되지 않은 피드",
"tabs.discover": "발견",
diff --git a/locales/mobile/default/pt.json b/locales/mobile/default/pt.json
index 29efabb9e..2c5b04587 100644
--- a/locales/mobile/default/pt.json
+++ b/locales/mobile/default/pt.json
@@ -1,4 +1,16 @@
{
+ "operation.change_to_other_view": "他のビューに切り替え",
+ "operation.copy_link": "リンクをコピー",
+ "operation.delete_category": "カテゴリーを削除",
+ "operation.edit": "編集",
+ "operation.mark_all_as_read": "すべてを既読にする",
+ "operation.mark_all_as_read_confirm": "すべてを既読にしますか?",
+ "operation.mark_all_as_read_which": "を既読にしますか?",
+ "operation.mark_as_read": "既読にする",
+ "operation.rename_category": "カテゴリーの名前を変更",
+ "operation.share": "共有",
+ "operation.star": "スター",
+ "operation.unfollow": "フォロー解除",
"profile.title": "Perfil de {{name}}",
"profile.uncategorized_feeds": "Feeds não categorizados",
"tabs.discover": "Descobrir",
diff --git a/locales/mobile/default/ru.json b/locales/mobile/default/ru.json
index d6a2eec62..32973f26e 100644
--- a/locales/mobile/default/ru.json
+++ b/locales/mobile/default/ru.json
@@ -1,4 +1,17 @@
{
+ "operation.add_feeds_to_category": "Переместить в категорию",
+ "operation.change_to_other_view": "Изменить на другой вид",
+ "operation.copy_link": "Копировать ссылку",
+ "operation.delete_category": "Удалить категорию",
+ "operation.edit": "Редактировать",
+ "operation.mark_all_as_read": "Отметить все как прочитанное",
+ "operation.mark_all_as_read_confirm": "Подтвердить отметку всего как прочитанное?",
+ "operation.mark_all_as_read_which": "Отметить как прочитанное?",
+ "operation.mark_as_read": "Отметить как прочитанное",
+ "operation.rename_category": "Переименовать категорию",
+ "operation.share": "Поделиться",
+ "operation.star": "Добавить в избранное",
+ "operation.unfollow": "Отписаться",
"profile.title": "Профиль {{name}}",
"profile.uncategorized_feeds": "Некатегоризированные ленты",
"tabs.discover": "Открыть",
diff --git a/locales/mobile/default/tr.json b/locales/mobile/default/tr.json
index f8938ad97..c19c224df 100644
--- a/locales/mobile/default/tr.json
+++ b/locales/mobile/default/tr.json
@@ -1,4 +1,16 @@
{
+ "operation.change_to_other_view": "Diğer görünüme geç",
+ "operation.copy_link": "Bağlantıyı kopyala",
+ "operation.delete_category": "Kategoriyi sil",
+ "operation.edit": "Düzenle",
+ "operation.mark_all_as_read": "Tümünü okundu olarak işaretle",
+ "operation.mark_all_as_read_confirm": "Tümünü okundu olarak işaretlemeyi onaylıyor musunuz?",
+ "operation.mark_all_as_read_which": " okundu olarak işaretlensin mi?",
+ "operation.mark_as_read": "Okundu olarak işaretle",
+ "operation.rename_category": "Kategoriyi yeniden adlandır",
+ "operation.share": "Paylaş",
+ "operation.star": "Yıldızla",
+ "operation.unfollow": "Takibi bırak",
"profile.title": "{{name}}'nin Profili",
"profile.uncategorized_feeds": "Kategorize edilmemiş beslemeler",
"tabs.discover": "Keşfet",
diff --git a/locales/mobile/default/zh-CN.json b/locales/mobile/default/zh-CN.json
index 486d21278..991ca2026 100644
--- a/locales/mobile/default/zh-CN.json
+++ b/locales/mobile/default/zh-CN.json
@@ -1,4 +1,23 @@
{
+ "operation.add_feeds_to_category": "移动至分类",
+ "operation.change_to_other_view": "更改为其他视图",
+ "operation.copy_link": "复制链接",
+ "operation.delete_category": "删除分类",
+ "operation.edit": "编辑",
+ "operation.mark_all_as_read": "全部标记为已读",
+ "operation.mark_all_as_read_confirm": "确认将全部标记为已读?",
+ "operation.mark_all_as_read_which": "将 标记为已读?",
+ "operation.mark_as_read": "标记为已读",
+ "operation.open_link": "打开链接",
+ "operation.rename_category": "重命名分类",
+ "operation.share": "分享",
+ "operation.star": "收藏",
+ "operation.toggle_unread_only.show_all.label": "显示全部",
+ "operation.toggle_unread_only.show_all.success": "正在显示所有条目",
+ "operation.toggle_unread_only.show_unread_only.label": "仅显示未读",
+ "operation.toggle_unread_only.show_unread_only.success": "仅显示未读条目",
+ "operation.unfollow": "取消订阅",
+ "operation.unstar": "取消收藏",
"profile.title": "{{name}}的个人资料",
"profile.uncategorized_feeds": "未分类的订阅源",
"tabs.discover": "发现",
diff --git a/locales/mobile/default/zh-HK.json b/locales/mobile/default/zh-HK.json
index 74b6c2b1d..5eb4f7c03 100644
--- a/locales/mobile/default/zh-HK.json
+++ b/locales/mobile/default/zh-HK.json
@@ -1,4 +1,17 @@
{
+ "operation.add_feeds_to_category": "移至分類",
+ "operation.change_to_other_view": "切換到其他視圖",
+ "operation.copy_link": "複製連結",
+ "operation.delete_category": "刪除分類",
+ "operation.edit": "編輯",
+ "operation.mark_all_as_read": "全部標記為已讀",
+ "operation.mark_all_as_read_confirm": "確定將所有標記為已讀?",
+ "operation.mark_all_as_read_which": "確定將 標記為已讀?",
+ "operation.mark_as_read": "標記為已讀",
+ "operation.rename_category": "重新命名分類",
+ "operation.share": "分享",
+ "operation.star": "收藏",
+ "operation.unfollow": "取消追隨",
"profile.title": "{{name}}的個人資料",
"profile.uncategorized_feeds": "未分類的訂閱源",
"tabs.discover": "探索",
diff --git a/locales/mobile/default/zh-TW.json b/locales/mobile/default/zh-TW.json
index 55220fbd4..4d373364d 100644
--- a/locales/mobile/default/zh-TW.json
+++ b/locales/mobile/default/zh-TW.json
@@ -1,4 +1,17 @@
{
+ "operation.add_feeds_to_category": "新增 RSS 摘要到類別",
+ "operation.change_to_other_view": "切換到其他視圖",
+ "operation.copy_link": "複製連結",
+ "operation.delete_category": "刪除類別",
+ "operation.edit": "編輯",
+ "operation.mark_all_as_read": "全部標記為已讀",
+ "operation.mark_all_as_read_confirm": "確認將全部標記為已讀?",
+ "operation.mark_all_as_read_which": "將 標記為已讀?",
+ "operation.mark_as_read": "標記為已讀",
+ "operation.rename_category": "重新命名類別",
+ "operation.share": "分享",
+ "operation.star": "收藏",
+ "operation.unfollow": "取消跟隨",
"profile.title": "{{name}} 的個人資料",
"profile.uncategorized_feeds": "未分類的 RSS 摘要",
"tabs.discover": "探索",
diff --git a/package.json b/package.json
index 53904aef6..2b75cae95 100644
--- a/package.json
+++ b/package.json
@@ -13,7 +13,7 @@
},
"scripts": {
"build:web": "turbo run Folo#build:web",
- "dedupe:locales": "eslint --fix locales/**/*.json",
+ "dedupe:locales": "eslint --fix locales/**",
"depcheck": "npx depcheck --quiet",
"format": "prettier --write .",
"format:check": "prettier --check .",
diff --git a/scripts/copy-translation.ts b/scripts/copy-translation.ts
index 3a86c260a..2bce91ade 100644
--- a/scripts/copy-translation.ts
+++ b/scripts/copy-translation.ts
@@ -1,20 +1,38 @@
import fs from "node:fs"
import path from "node:path"
-const sourceDir = "./locales/common"
-const targetDir = "./locales/common"
+const sourceDir = "./locales/app"
+const targetDir = "./locales/mobile/default"
const keysToCopy: string[] = [
- // // kept the duplicate keys in locales/app
- // "words.starred",
- // "words.inbox",
- // "words.feeds",
- // "words.lists",
- // "words.search",
- "cancel",
- "close",
- "confirm",
+ "sidebar.feed_column.context_menu.mark_as_read",
+ "entry_actions.star",
+ "entry_actions.share",
+ "mark_all_read_button.mark_all_as_read",
+ "sidebar.feed_column.context_menu.add_feeds_to_category",
+ "sidebar.feed_actions.edit",
+ "entry_actions.copy_link",
+ "sidebar.feed_actions.unfollow",
+ "sidebar.feed_column.context_menu.change_to_other_view",
+ "sidebar.feed_column.context_menu.rename_category",
+ "sidebar.feed_column.context_menu.delete_category",
+ "mark_all_read_button.confirm_mark_all",
+ "mark_all_read_button.confirm_mark_all_info",
+]
+const keysToPlace = [
+ "operation.mark_as_read",
+ "operation.star",
+ "operation.share",
+ "operation.mark_all_as_read",
+ "operation.add_feeds_to_category",
+ "operation.edit",
+ "operation.copy_link",
+ "operation.unfollow",
+ "operation.change_to_other_view",
+ "operation.rename_category",
+ "operation.delete_category",
+ "operation.mark_all_as_read_which",
+ "operation.mark_all_as_read_confirm",
]
-const keysToPlace = ["words.cancel", "words.close", "words.confirm"]
const copyTranslations = (sourceDir: string, targetDir: string) => {
const sourceFiles = fs.readdirSync(sourceDir)