feat(mobile): translation for home tabs (#3386)

* feat(mobile): translation content

* Starred

* inbox feeds lists

* profile

* discovery category

* search

* dayjs

* bring localizations back

* translation for mobile content

* todo note

* add settings resource

* settings

* Extract

* Extract

* Manually type

* extend

* remove feed_view_type

* remove discover.category

* update note

* type
This commit is contained in:
Stephen Zhou 2025-04-02 09:44:26 +08:00 committed by GitHub
parent e7ae1f69ad
commit 20f0abe302
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
92 changed files with 1020 additions and 420 deletions

View File

@ -39,7 +39,7 @@ export const RecommendationCard: FC<RecommendationCardProps> = memo(
categories.delete("popular")
return {
maintainers: Array.from(maintainers),
categories: Array.from(categories) as typeof RSSHubCategories | string[],
categories: Array.from(categories) as unknown as typeof RSSHubCategories,
}
}, [data])
@ -146,7 +146,7 @@ export const RecommendationCard: FC<RecommendationCardProps> = memo(
)}
>
{RSSHubCategories.includes(c)
? t(`discover.category.${c as (typeof RSSHubCategories)[number]}`)
? t(`discover.category.${c}`, { ns: "common" })
: upperFirst(c)}
</button>
))}

View File

@ -176,7 +176,7 @@ export function Recommendations({
<TabsList>
{RSSHubCategories.map((category) => (
<TabsTrigger data-value={category} key={category} value={category}>
{t(`discover.category.${category}`)}
{t(`discover.category.${category}`, { ns: "common" })}
</TabsTrigger>
))}
</TabsList>

View File

@ -12,7 +12,7 @@ export const ViewSelectContent = () => {
<SelectItem key={view.name} value={`${index}`}>
<div className="flex items-center gap-2">
<span className={cn(view.className, "flex")}>{view.icon}</span>
<span>{t(view.name as any)}</span>
<span>{t(view.name, { ns: "common" })}</span>
</div>
</SelectItem>
))}

View File

@ -162,7 +162,9 @@ export const SettingLists = () => {
</span>
</TooltipTrigger>
<TooltipPortal>
<TooltipContent>{t(views[row.view]!.name as any)}</TooltipContent>
<TooltipContent>
{t(views[row.view]!.name, { ns: "common" })}
</TooltipContent>
</TooltipPortal>
</Tooltip>
</TableCell>

View File

@ -46,7 +46,9 @@ export const ViewSelectorRadioGroup = forwardRef<
{cloneElement(view.icon, {
className: `text-lg ${view.icon?.props?.className ?? ""}`,
})}
<span className="mt-1 hidden text-xs lg:inline">{t(view.name as any)}</span>
<span className="mt-1 hidden text-xs lg:inline">
{t(view.name, { ns: "common" })}
</span>
</label>
</div>
))}

View File

@ -240,7 +240,7 @@ function FeedCategoryImpl({ data: ids, view, categoryOpenStateData }: FeedCatego
submenu: views
.filter((v) => v.view !== view)
.map((v) => ({
label: t(v.name as any),
label: t(v.name, { ns: "common" }),
type: "text" as const,
shortcut: (v.view + 1).toString(),
icon: v.icon,

View File

@ -96,7 +96,7 @@ const FeedListImpl = ({ className, view }: { className?: string; view: number })
<span className="ml-2">
{t("words.all", { ns: "common" })}
{t("space", { ns: "common" })}
{t(views[view]!.name as any)}
{t(views[view]!.name, { ns: "common" })}
</span>
</button>
<SortableFeedList

View File

@ -64,7 +64,7 @@ export const ListHeader = ({ view }: { view: number }) => {
}
}}
>
{view !== undefined && t(views[view]!.name as any)}
{view !== undefined && t(views[view]!.name, { ns: "common" })}
</div>
<div className="lg:!text-theme-vibrancyFg ml-2 flex items-center gap-3 text-base text-zinc-400 lg:text-sm dark:text-zinc-600">
<SortButton />

View File

@ -56,7 +56,7 @@ const ViewSwitchButton: FC<{
<ActionButton
ref={setNodeRef}
key={item.name}
tooltip={t(item.name as any)}
tooltip={t(item.name, { ns: "common" })}
shortcut={`${view + 1}`}
className={cn(
isActive && item.className,

View File

@ -78,7 +78,7 @@ export const useFeedHeaderTitle = () => {
switch (currentFeedId) {
case ROUTE_FEED_PENDING: {
return t(views[view]!.name as any)
return t(views[view]!.name, { ns: "common" })
}
case FEED_COLLECTION_LIST: {
return t("words.starred")

View File

@ -51,7 +51,28 @@ export default ({ config }: ConfigContext): ExpoConfig => ({
LSApplicationQueriesSchemes: ["bilibili", "youtube"],
CFBundleAllowMixedLocalizations: true,
// apps/mobile/src/@types/constants.ts currentSupportedLanguages
CFBundleLocalizations: ["en", "zh-CN"],
CFBundleLocalizations: [
"en",
"de",
"ja",
"zh-CN",
"zh-TW",
"zh-HK",
"pt",
"fr",
"ar-DZ",
"ar-SA",
"ar-MA",
"ar-IQ",
"ar-KW",
"ar-TN",
"fi",
"it",
"ru",
"es",
"ko",
"tr",
],
CFBundleDevelopmentRegion: "en",
},
googleServicesFile: "./build/GoogleService-Info.plist",

View File

@ -1,6 +1,50 @@
const langs = ["en", "zh-CN"] as const
const langs = [
"en",
"de",
"ja",
"zh-CN",
"zh-TW",
"zh-HK",
"pt",
"fr",
"ar-DZ",
"ar-SA",
"ar-MA",
"ar-IQ",
"ar-KW",
"ar-TN",
"fi",
"it",
"ru",
"es",
"ko",
"tr",
] as const
export const currentSupportedLanguages = langs as readonly string[]
export type MobileSupportedLanguages = (typeof langs)[number]
export const ns = ["default", "common", "lang", "errors"] as const
export const ns = ["default", "common", "lang", "errors", "settings"] as const
export const defaultNS = "default" as const
export const dayjsLocaleImportMap = {
en: ["en", () => import("dayjs/locale/en")],
["zh-CN"]: ["zh-cn", () => import("dayjs/locale/zh-cn")],
["ja"]: ["ja", () => import("dayjs/locale/ja")],
["ru"]: ["ru", () => import("dayjs/locale/ru")],
["fi"]: ["fi", () => import("dayjs/locale/fi")],
["it"]: ["it", () => import("dayjs/locale/it")],
["ar-DZ"]: ["ar-dz", () => import("dayjs/locale/ar-dz")],
["ar-SA"]: ["ar-sa", () => import("dayjs/locale/ar-sa")],
["ar-MA"]: ["ar-ma", () => import("dayjs/locale/ar-ma")],
["es"]: ["es", () => import("dayjs/locale/es")],
["fr"]: ["fr", () => import("dayjs/locale/fr")],
["pt"]: ["pt", () => import("dayjs/locale/pt")],
["zh-TW"]: ["zh-tw", () => import("dayjs/locale/zh-tw")],
["ar-IQ"]: ["ar-iq", () => import("dayjs/locale/ar-iq")],
["ar-KW"]: ["ar-kw", () => import("dayjs/locale/ar-kw")],
["ar-TN"]: ["ar-tn", () => import("dayjs/locale/ar-tn")],
["zh-HK"]: ["zh-hk", () => import("dayjs/locale/zh-hk")],
["de"]: ["de", () => import("dayjs/locale/de")],
["ko"]: ["ko", () => import("dayjs/locale/ko")],
["tr"]: ["tr", () => import("dayjs/locale/tr")],
} as const

View File

@ -1,26 +1,249 @@
import common_ardz from "@locales/common/ar-DZ.json"
import common_ariq from "@locales/common/ar-IQ.json"
import common_arkw from "@locales/common/ar-KW.json"
import common_arma from "@locales/common/ar-MA.json"
import common_arsa from "@locales/common/ar-SA.json"
import common_artn from "@locales/common/ar-TN.json"
import common_de from "@locales/common/de.json"
import common_en from "@locales/common/en.json"
import common_es from "@locales/common/es.json"
import common_fi from "@locales/common/fi.json"
import common_fr from "@locales/common/fr.json"
import common_it from "@locales/common/it.json"
import common_ja from "@locales/common/ja.json"
import common_ko from "@locales/common/ko.json"
import common_pt from "@locales/common/pt.json"
import common_ru from "@locales/common/ru.json"
import common_tr from "@locales/common/tr.json"
import common_zhCN from "@locales/common/zh-CN.json"
import common_zhHK from "@locales/common/zh-HK.json"
import common_zhTW from "@locales/common/zh-TW.json"
import errors_de from "@locales/errors/de.json"
import errors_en from "@locales/errors/en.json"
import errors_fr from "@locales/errors/fr.json"
import errors_ja from "@locales/errors/ja.json"
import errors_ko from "@locales/errors/ko.json"
import errors_ru from "@locales/errors/ru.json"
import errors_tr from "@locales/errors/tr.json"
import errors_zhCN from "@locales/errors/zh-CN.json"
import errors_zhHK from "@locales/errors/zh-HK.json"
import errors_zhTW from "@locales/errors/zh-TW.json"
import lang_ardz from "@locales/lang/ar-DZ.json"
import lang_ariq from "@locales/lang/ar-IQ.json"
import lang_arkw from "@locales/lang/ar-KW.json"
import lang_arma from "@locales/lang/ar-MA.json"
import lang_arsa from "@locales/lang/ar-SA.json"
import lang_artn from "@locales/lang/ar-TN.json"
import lang_de from "@locales/lang/de.json"
import lang_en from "@locales/lang/en.json"
import lang_es from "@locales/lang/es.json"
import lang_fi from "@locales/lang/fi.json"
import lang_fr from "@locales/lang/fr.json"
import lang_it from "@locales/lang/it.json"
import lang_ja from "@locales/lang/ja.json"
import lang_ko from "@locales/lang/ko.json"
import lang_pt from "@locales/lang/pt.json"
import lang_ru from "@locales/lang/ru.json"
import lang_tr from "@locales/lang/tr.json"
import lang_zhCN from "@locales/lang/zh-CN.json"
import lang_zhHK from "@locales/lang/zh-HK.json"
import lang_zhTW from "@locales/lang/zh-TW.json"
import arDZ from "@locales/mobile/default/ar-DZ.json"
import arIQ from "@locales/mobile/default/ar-IQ.json"
import arKW from "@locales/mobile/default/ar-KW.json"
import arMA from "@locales/mobile/default/ar-MA.json"
import arSA from "@locales/mobile/default/ar-SA.json"
import arTN from "@locales/mobile/default/ar-TN.json"
import de from "@locales/mobile/default/de.json"
import en from "@locales/mobile/default/en.json"
import es from "@locales/mobile/default/es.json"
import fi from "@locales/mobile/default/fi.json"
import fr from "@locales/mobile/default/fr.json"
import it from "@locales/mobile/default/it.json"
import ja from "@locales/mobile/default/ja.json"
import ko from "@locales/mobile/default/ko.json"
import pt from "@locales/mobile/default/pt.json"
import ru from "@locales/mobile/default/ru.json"
import tr from "@locales/mobile/default/tr.json"
import zhCN from "@locales/mobile/default/zh-CN.json"
import zhHK from "@locales/mobile/default/zh-HK.json"
import zhTW from "@locales/mobile/default/zh-TW.json"
import settings_ardz from "@locales/settings/ar-DZ.json"
import settings_ariq from "@locales/settings/ar-IQ.json"
import settings_arkw from "@locales/settings/ar-KW.json"
import settings_arma from "@locales/settings/ar-MA.json"
import settings_arsa from "@locales/settings/ar-SA.json"
import settings_artn from "@locales/settings/ar-TN.json"
import settings_de from "@locales/settings/de.json"
import settings_en from "@locales/settings/en.json"
import settings_es from "@locales/settings/es.json"
import settings_fi from "@locales/settings/fi.json"
import settings_fr from "@locales/settings/fr.json"
import settings_it from "@locales/settings/it.json"
import settings_ja from "@locales/settings/ja.json"
import settings_ko from "@locales/settings/ko.json"
import settings_pt from "@locales/settings/pt.json"
import settings_ru from "@locales/settings/ru.json"
import settings_tr from "@locales/settings/tr.json"
import settings_zhCN from "@locales/settings/zh-CN.json"
import settings_zhHK from "@locales/settings/zh-HK.json"
import settings_zhTW from "@locales/settings/zh-TW.json"
import type { MobileSupportedLanguages, ns } from "./constants"
// @keep-sorted
export const defaultResources = {
// @keep-sorted
"ar-DZ": {
common: common_ardz,
default: arDZ,
lang: lang_ardz,
settings: settings_ardz,
},
// @keep-sorted
"ar-IQ": {
common: common_ariq,
default: arIQ,
lang: lang_ariq,
settings: settings_ariq,
},
// @keep-sorted
"ar-KW": {
common: common_arkw,
default: arKW,
lang: lang_arkw,
settings: settings_arkw,
},
// @keep-sorted
"ar-MA": {
common: common_arma,
default: arMA,
lang: lang_arma,
settings: settings_arma,
},
// @keep-sorted
"ar-SA": {
common: common_arsa,
default: arSA,
lang: lang_arsa,
settings: settings_arsa,
},
// @keep-sorted
"ar-TN": {
common: common_artn,
default: arTN,
lang: lang_artn,
settings: settings_artn,
},
// @keep-sorted
"zh-CN": {
default: zhCN,
common: common_zhCN,
default: zhCN,
errors: errors_zhCN,
lang: lang_zhCN,
settings: settings_zhCN,
},
// @keep-sorted
"zh-HK": {
common: common_zhHK,
default: zhHK,
errors: errors_zhHK,
lang: lang_zhHK,
settings: settings_zhHK,
},
// @keep-sorted
"zh-TW": {
common: common_zhTW,
default: zhTW,
errors: errors_zhTW,
lang: lang_zhTW,
settings: settings_zhTW,
},
// @keep-sorted
de: {
common: common_de,
default: de,
errors: errors_de,
lang: lang_de,
settings: settings_de,
},
// @keep-sorted
en: {
default: en,
common: common_en,
default: en,
errors: errors_en,
lang: lang_en,
settings: settings_en,
},
} satisfies Record<MobileSupportedLanguages, Record<(typeof ns)[number], Record<string, string>>>
// @keep-sorted
es: {
common: common_es,
default: es,
lang: lang_es,
settings: settings_es,
},
// @keep-sorted
fi: {
common: common_fi,
default: fi,
lang: lang_fi,
settings: settings_fi,
},
// @keep-sorted
fr: {
common: common_fr,
default: fr,
errors: errors_fr,
lang: lang_fr,
settings: settings_fr,
},
// @keep-sorted
it: {
common: common_it,
default: it,
lang: lang_it,
settings: settings_it,
},
// @keep-sorted
ja: {
common: common_ja,
default: ja,
errors: errors_ja,
lang: lang_ja,
settings: settings_ja,
},
// @keep-sorted
ko: {
common: common_ko,
default: ko,
errors: errors_ko,
lang: lang_ko,
settings: settings_ko,
},
// @keep-sorted
pt: {
common: common_pt,
default: pt,
lang: lang_pt,
settings: settings_pt,
},
// @keep-sorted
ru: {
common: common_ru,
default: ru,
errors: errors_ru,
lang: lang_ru,
settings: settings_ru,
},
// @keep-sorted
tr: {
common: common_tr,
default: tr,
errors: errors_tr,
lang: lang_tr,
settings: settings_tr,
},
} satisfies Record<
MobileSupportedLanguages,
Partial<Record<(typeof ns)[number], Record<string, string>>>
>

View File

@ -1,5 +1,6 @@
import dayjs from "dayjs"
import { useEffect, useMemo, useState } from "react"
import { useTranslation } from "react-i18next"
import type { TextProps } from "react-native"
import { Pressable } from "react-native"
import Animated, { FadeOut } from "react-native-reanimated"
@ -49,9 +50,10 @@ export const RelativeDateTime = ({
date,
displayAbsoluteTimeAfterDay,
dateFormatTemplate,
postfixText = "ago",
postfixText,
...props
}: RelativeDateTimeProps) => {
const { t } = useTranslation("common")
const [relative, setRelative] = useState<string>(() =>
formatTime(date, displayAbsoluteTimeAfterDay, dateFormatTemplate),
)
@ -80,7 +82,9 @@ export const RelativeDateTime = ({
}}
>
<Animated.Text {...props} key={mode} exiting={FadeOut}>
{mode === "relative" ? `${relative} ${postfixText}` : memoizedFormatTime}
{mode === "relative"
? `${relative}${t("space")}${postfixText ?? t("words.ago")}`
: memoizedFormatTime}
</Animated.Text>
</Pressable>
)

View File

@ -1,5 +1,5 @@
import { FeedViewType } from "@follow/constants"
import type * as React from "react"
import type { ViewDefinition as ViewDefinitionBase } from "@follow/constants"
import { FeedViewType, views as viewsBase } from "@follow/constants"
import colors from "tailwindcss/colors"
import { AnnouncementCuteFiIcon } from "../icons/announcement_cute_fi"
@ -10,56 +10,43 @@ import { TwitterCuteFiIcon } from "../icons/twitter_cute_fi"
import { VideoCuteFiIcon } from "../icons/video_cute_fi"
import { accentColor } from "../theme/colors"
export interface ViewDefinition {
name: string
interface ViewDefinitionExtended {
icon: React.FC<{ color?: string; height?: number; width?: number }>
activeColor: string
translation: string
view: FeedViewType
wideMode?: boolean
gridMode?: boolean
}
export const views: ViewDefinition[] = [
{
name: "Articles",
const extendMap: Record<FeedViewType, ViewDefinitionExtended> = {
[FeedViewType.Articles]: {
icon: PaperCuteFiIcon,
activeColor: accentColor,
translation: "title,description",
view: FeedViewType.Articles,
},
{
name: "Social Media",
[FeedViewType.SocialMedia]: {
icon: TwitterCuteFiIcon,
activeColor: colors.sky[500],
translation: "content",
view: FeedViewType.SocialMedia,
},
{
name: "Pictures",
[FeedViewType.Pictures]: {
icon: PicCuteFiIcon,
activeColor: colors.green[500],
translation: "title",
view: FeedViewType.Pictures,
},
{
name: "Videos",
[FeedViewType.Videos]: {
icon: VideoCuteFiIcon,
activeColor: colors.red[500],
translation: "title",
view: FeedViewType.Videos,
},
{
name: "Audios",
[FeedViewType.Audios]: {
icon: MicCuteFiIcon,
activeColor: colors.purple[500],
translation: "title",
view: FeedViewType.Audios,
},
{
name: "Notifications",
[FeedViewType.Notifications]: {
icon: AnnouncementCuteFiIcon,
activeColor: colors.yellow[500],
translation: "title",
view: FeedViewType.Notifications,
},
]
}
export interface ViewDefinition extends Omit<ViewDefinitionBase, "icon">, ViewDefinitionExtended {}
export const views: ViewDefinition[] = viewsBase.map((view) => {
const extendedView = extendMap[view.view]
return {
...view,
...extendedView,
}
})

View File

@ -1,27 +1,46 @@
import dayjs from "dayjs"
import { getLocales } from "expo-localization"
import i18n from "i18next"
import { initReactI18next } from "react-i18next"
import { currentSupportedLanguages, defaultNS, ns } from "@/src/@types/constants"
import {
currentSupportedLanguages,
dayjsLocaleImportMap,
defaultNS,
ns,
} from "@/src/@types/constants"
import { defaultResources } from "@/src/@types/default-resource"
import { getGeneralSettings } from "../atoms/settings/general"
const fallbackLanguage = "en"
export const updateDayjsLocale = async (lang: string) => {
if (!(lang in dayjsLocaleImportMap)) return
const dayjsImport = dayjsLocaleImportMap[lang as keyof typeof dayjsLocaleImportMap]
const [locale, loader] = dayjsImport
await loader()
dayjs.locale(locale)
}
export async function initializeI18n() {
return i18n.use(initReactI18next).init({
ns,
defaultNS,
resources: defaultResources,
const { language } = getGeneralSettings()
lng: getGeneralSettings().language,
fallbackLng: fallbackLanguage,
return Promise.all([
updateDayjsLocale(language),
i18n.use(initReactI18next).init({
ns,
defaultNS,
resources: defaultResources,
interpolation: {
escapeValue: false,
},
})
lng: language,
fallbackLng: fallbackLanguage,
interpolation: {
escapeValue: false,
},
}),
])
}
export function getDeviceLanguage() {

View File

@ -2,6 +2,7 @@ import type { RSSHubCategories } from "@follow/constants"
import type { RSSHubRouteDeclaration } from "@follow/models/src/rsshub"
import type { FC } from "react"
import { memo, useMemo } from "react"
import { useTranslation } from "react-i18next"
import { Clipboard, Text, TouchableOpacity, View } from "react-native"
import WebView from "react-native-webview"
import * as ContextMenu from "zeego/context-menu"
@ -13,12 +14,11 @@ import { useNavigation } from "@/src/lib/navigation/hooks"
import { toast } from "@/src/lib/toast"
import { RsshubFormScreen } from "@/src/screens/(modal)/rsshub-form"
import { RSSHubCategoryCopyMap } from "./copy"
export const RecommendationListItem: FC<{
data: RSSHubRouteDeclaration
routePrefix: string
}> = memo(({ data, routePrefix }) => {
const { t } = useTranslation("common")
const { maintainers, categories } = useMemo(() => {
const maintainers = new Set<string>()
const categories = new Set<string>()
@ -34,7 +34,7 @@ export const RecommendationListItem: FC<{
categories.delete("popular")
return {
maintainers: Array.from(maintainers),
categories: Array.from(categories) as typeof RSSHubCategories | string[],
categories: Array.from(categories) as unknown as typeof RSSHubCategories,
}
}, [data])
@ -56,7 +56,7 @@ export const RecommendationListItem: FC<{
key={c}
>
<Text className="text-text/60 text-xs" numberOfLines={1}>
{RSSHubCategoryCopyMap[c as keyof typeof RSSHubCategoryCopyMap]}
{t(`discover.category.${c}`)}
</Text>
</View>
))}

View File

@ -6,6 +6,7 @@ import { useQuery } from "@tanstack/react-query"
import { useAtomValue } from "jotai"
import type { FC } from "react"
import { memo, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react"
import { useTranslation } from "react-i18next"
import type { ScrollView } from "react-native"
import {
ActivityIndicator,
@ -26,11 +27,11 @@ import {
import type { TabComponent } from "@/src/components/ui/tabview/TabView"
import { apiClient } from "@/src/lib/api-fetch"
import { RSSHubCategoryCopyMap } from "./copy"
import { DiscoverContext } from "./DiscoverContext"
import { RecommendationListItem } from "./RecommendationListItem"
export const Recommendations = () => {
const { t } = useTranslation("common")
const { animatedX, currentTabAtom } = useContext(DiscoverContext)
const currentTab = useAtomValue(currentTabAtom)
@ -65,7 +66,7 @@ export const Recommendations = () => {
{loadedTabIndex.has(index) && (
<Tab
key={category}
tab={{ name: RSSHubCategoryCopyMap[category], value: category }}
tab={{ name: t(`discover.category.${category}`), value: category }}
isSelected={currentTab === index}
/>
)}

View File

@ -1,25 +0,0 @@
export const RSSHubCategoryCopyMap = {
all: "All",
anime: "ACG",
bbs: "BBS",
blog: "Blog",
design: "Design",
finance: "Finance",
forecast: "Forecast",
game: "Gaming",
government: "Government",
journal: "Scientific Journal",
live: "Live",
multimedia: "Multimedia",
"new-media": "New Media",
picture: "Picture",
"program-update": "Application Updates",
programming: "Programming",
reading: "Reading",
shopping: "Shopping",
"social-media": "Social Media",
study: "Study",
"traditional-media": "News",
travel: "Travel",
university: "University",
}

View File

@ -2,6 +2,7 @@ import { RSSHubCategories } from "@follow/constants"
import { useAtom, useAtomValue, useSetAtom } from "jotai"
import type { FC } from "react"
import { useContext, useEffect, useRef, useState } from "react"
import { useTranslation } from "react-i18next"
import type { Animated as RnAnimated, LayoutChangeEvent } from "react-native"
import { Pressable, StyleSheet, Text, TextInput, TouchableOpacity, View } from "react-native"
import Animated, {
@ -22,7 +23,6 @@ import SearchScreen from "@/src/screens/(headless)/search"
import { accentColor, useColor } from "@/src/theme/colors"
import { AddFeedButton } from "../screen/action"
import { RSSHubCategoryCopyMap } from "./copy"
import { useSearchPageContext } from "./ctx"
import { DiscoverContext } from "./DiscoverContext"
import { SearchTabBar } from "./SearchTabBar"
@ -68,6 +68,7 @@ export const DiscoverHeader = () => {
return <DiscoverHeaderImpl />
}
const DiscoverHeaderImpl = () => {
const { t } = useTranslation("common")
const frame = useSafeAreaFrame()
const insets = useSafeAreaInsets()
const sheetModal = useScreenIsInSheetModal()
@ -97,7 +98,7 @@ const DiscoverHeaderImpl = () => {
<TabBar
tabs={RSSHubCategories.map((category) => ({
name: RSSHubCategoryCopyMap[category],
name: t(`discover.category.${category}`),
value: category,
}))}
tabScrollContainerAnimatedX={animatedX}
@ -112,6 +113,7 @@ const DiscoverHeaderImpl = () => {
const PlaceholerSearchBar = () => {
const labelColor = useColor("secondaryLabel")
const { t } = useTranslation("common")
return (
<Pressable
style={styles.searchbar}
@ -126,7 +128,7 @@ const PlaceholerSearchBar = () => {
>
<Search2CuteReIcon color={labelColor} height={18} width={18} />
<Text className="text-secondary-label ml-1" style={styles.searchPlaceholderText}>
Search
{t("words.search")}
</Text>
</View>
</Pressable>
@ -159,6 +161,7 @@ const ComposeSearchBar = () => {
}
const SearchInput = () => {
const { t } = useTranslation("common")
const { searchFocusedAtom, searchValueAtom } = useSearchPageContext()
const [isFocused, setIsFocused] = useAtom(searchFocusedAtom)
const placeholderTextColor = useColor("secondaryLabel")
@ -232,7 +235,7 @@ const SearchInput = () => {
<Search2CuteReIcon color={placeholderTextColor} height={18} width={18} />
{!searchValue && !tempSearchValue && (
<Text className="text-secondary-label ml-2" style={styles.searchPlaceholderText}>
Search
{t("words.search")}
</Text>
)}
</Animated.View>
@ -260,7 +263,7 @@ const SearchInput = () => {
<Animated.View style={skeletonAnimatedStyle} pointerEvents="none">
<Search2CuteReIcon color={placeholderTextColor} height={18} width={18} />
<Text className="text-secondary-label ml-1" style={styles.searchPlaceholderText}>
Search
{t("words.search")}
</Text>
</Animated.View>
</View>

View File

@ -131,7 +131,6 @@ export function EntryNormalItem({ entryId, extraData }: { entryId: string; extra
<RelativeDateTime
date={entry.publishedAt}
className="text-secondary-label text-xs font-medium"
postfixText="ago"
/>
</View>
{!!entry.title && (

View File

@ -1,4 +1,5 @@
import { useEffect } from "react"
import { useTranslation } from "react-i18next"
import type { StyleProp, ViewStyle } from "react-native"
import { ScrollView, useWindowDimensions, View } from "react-native"
import Animated, {
@ -106,6 +107,7 @@ function ViewItem({ view }: { view: ViewDefinition }) {
const isActive = selectedFeed?.type === "view" && selectedFeed.viewId === view.view
const unreadCount = useUnreadCountByView(view.view)
const borderColor = useColor("gray5")
const { t } = useTranslation("common")
return (
<TimelineViewSelectorContextMenu type="view" viewId={view.view}>
@ -123,7 +125,7 @@ function ViewItem({ view }: { view: ViewDefinition }) {
className="text-sm font-semibold text-white"
numberOfLines={1}
>
{view.name}
{t(view.name)}
</Animated.Text>
{!!unreadCount && (
<Animated.View exiting={FadeOut} className="size-1.5 rounded-full bg-white" />

View File

@ -3,6 +3,7 @@ import { jotaiStore } from "@follow/utils"
import { EventBus } from "@follow/utils/src/event-bus"
import { atom, useAtom, useAtomValue, useSetAtom } from "jotai"
import { createContext, useCallback, useContext, useMemo } from "react"
import { useTranslation } from "react-i18next"
import { views } from "@/src/constants/views"
import { usePrefetchEntries } from "@/src/store/entry/hooks"
@ -191,6 +192,7 @@ export const useSelectedFeedTitle = () => {
const feed = useFeed(selectedFeed && selectedFeed.type === "feed" ? selectedFeed.feedId : "")
const list = useList(selectedFeed && selectedFeed.type === "list" ? selectedFeed.listId : "")
const inbox = useInbox(selectedFeed && selectedFeed.type === "inbox" ? selectedFeed.inboxId : "")
const { t } = useTranslation("common")
if (!selectedFeed) {
return ""
@ -198,7 +200,7 @@ export const useSelectedFeedTitle = () => {
switch (selectedFeed.type) {
case "view": {
return viewDef?.name
return viewDef?.name ? t(viewDef.name) : ""
}
case "feed": {
return selectedFeed.feedId === FEED_COLLECTION_LIST ? "Collections" : (feed?.title ?? "")
@ -210,7 +212,7 @@ export const useSelectedFeedTitle = () => {
return list?.title
}
case "inbox": {
return inbox?.title ?? "Inbox"
return inbox?.title ?? t("words.inbox")
}
}
}

View File

@ -1,7 +1,9 @@
import { UserRole } from "@follow/constants"
import * as FileSystem from "expo-file-system"
import type { ParseKeys } from "i18next"
import type { FC } from "react"
import { Fragment, useMemo } from "react"
import { useTranslation } from "react-i18next"
import { Alert, PixelRatio, View } from "react-native"
import {
@ -42,7 +44,7 @@ import { NotificationsScreen } from "./routes/Notifications"
import { PrivacyScreen } from "./routes/Privacy"
interface GroupNavigationLink {
label: string
label: Extract<ParseKeys<"settings">, `titles.${string}`>
icon: React.ElementType
onPress: (data: { navigation: Navigation }) => void
iconBackgroundColor: string
@ -53,7 +55,7 @@ interface GroupNavigationLink {
}
const SettingGroupNavigationLinks: GroupNavigationLink[] = [
{
label: "General",
label: "titles.general",
icon: Settings1CuteFiIcon,
onPress: ({ navigation }) => {
navigation.pushControllerView(GeneralScreen)
@ -61,7 +63,7 @@ const SettingGroupNavigationLinks: GroupNavigationLink[] = [
iconBackgroundColor: "#F59E0B",
},
{
label: "Notifications",
label: "titles.notifications",
icon: BellRingingCuteFiIcon,
onPress: ({ navigation }) => {
navigation.pushControllerView(NotificationsScreen)
@ -71,7 +73,7 @@ const SettingGroupNavigationLinks: GroupNavigationLink[] = [
anonymous: false,
},
{
label: "Appearance",
label: "titles.appearance",
icon: PaletteCuteFiIcon,
onPress: ({ navigation }) => {
navigation.pushControllerView(AppearanceScreen)
@ -79,7 +81,7 @@ const SettingGroupNavigationLinks: GroupNavigationLink[] = [
iconBackgroundColor: "#FCD34D",
},
{
label: "Data",
label: "titles.data_control",
icon: DatabaseIcon,
onPress: ({ navigation }) => {
navigation.pushControllerView(DataScreen)
@ -88,7 +90,7 @@ const SettingGroupNavigationLinks: GroupNavigationLink[] = [
anonymous: false,
},
{
label: "Account",
label: "titles.account",
icon: UserSettingCuteFiIcon,
onPress: ({ navigation }) => {
navigation.pushControllerView(AccountScreen)
@ -100,7 +102,7 @@ const SettingGroupNavigationLinks: GroupNavigationLink[] = [
const BetaGroupNavigationLinks: GroupNavigationLink[] = [
{
label: "Invitation",
label: "titles.invitations",
icon: LoveCuteFiIcon,
onPress: ({ navigation }) => {
navigation.pushControllerView(InvitationsScreen)
@ -113,7 +115,7 @@ const BetaGroupNavigationLinks: GroupNavigationLink[] = [
const DataGroupNavigationLinks: GroupNavigationLink[] = [
{
label: "Actions",
label: "titles.actions",
icon: Magic2CuteFiIcon,
onPress: ({ navigation }) => {
navigation.pushControllerView(ActionsScreen)
@ -124,7 +126,7 @@ const DataGroupNavigationLinks: GroupNavigationLink[] = [
},
{
label: "Feeds",
label: "titles.feeds",
icon: CertificateCuteFiIcon,
onPress: ({ navigation }) => {
navigation.pushControllerView(FeedsScreen)
@ -135,7 +137,7 @@ const DataGroupNavigationLinks: GroupNavigationLink[] = [
trialNotAllowed: true,
},
{
label: "Lists",
label: "titles.lists",
icon: RadaCuteFiIcon,
onPress: ({ navigation }) => {
navigation.pushControllerView(ListsScreen)
@ -149,7 +151,7 @@ const DataGroupNavigationLinks: GroupNavigationLink[] = [
const PrivacyGroupNavigationLinks: GroupNavigationLink[] = [
{
label: "Privacy",
label: "titles.privacy",
icon: SafeLockFilledIcon,
onPress: ({ navigation }) => {
navigation.pushControllerView(PrivacyScreen)
@ -157,7 +159,7 @@ const PrivacyGroupNavigationLinks: GroupNavigationLink[] = [
iconBackgroundColor: "#EF4444",
},
{
label: "About",
label: "titles.about",
icon: StarCuteFiIcon,
onPress: ({ navigation }) => {
navigation.pushControllerView(AboutScreen)
@ -168,7 +170,7 @@ const PrivacyGroupNavigationLinks: GroupNavigationLink[] = [
const ActionGroupNavigationLinks: GroupNavigationLink[] = [
{
label: "Sign out",
label: "titles.sign_out",
icon: ExitCuteFiIcon,
onPress: () => {
Alert.alert("Sign out", "Are you sure you want to sign out?", [
@ -196,6 +198,7 @@ const NavigationLinkGroup: FC<{
}> = ({ links }) => {
const navigation = useNavigation()
const role = useRole()
const { t } = useTranslation("settings")
return (
<GroupedInsetListCard>
@ -205,7 +208,7 @@ const NavigationLinkGroup: FC<{
return (
<GroupedInsetListNavigationLink
key={link.label}
label={link.label}
label={t(link.label)}
icon={
<GroupedInsetListNavigationLinkIcon backgroundColor={link.iconBackgroundColor}>
<link.icon height={18} width={18} color="#fff" />

View File

@ -18,6 +18,7 @@ import {
GroupedInsetListSectionHeader,
} from "@/src/components/ui/grouped/GroupedList"
import { Switch } from "@/src/components/ui/switch/Switch"
import { updateDayjsLocale } from "@/src/lib/i18n"
import type { NavigationControllerView } from "@/src/lib/navigation/types"
function LanguageSelect({ settingKey }: { settingKey: "language" | "actionLanguage" }) {
@ -47,6 +48,7 @@ function LanguageSelect({ settingKey }: { settingKey: "language" | "actionLangua
setGeneralSetting(settingKey, value)
if (settingKey === "language") {
i18next.changeLanguage(value)
updateDayjsLocale(value)
}
}}
displayValue={t(`langs.${language}` as any)}

View File

@ -1,6 +1,8 @@
import type { FeedViewType } from "@follow/constants"
import type { FlashList } from "@shopify/flash-list"
import type { ParseKeys } from "i18next"
import { useMemo, useState } from "react"
import { useTranslation } from "react-i18next"
import { Text, View } from "react-native"
import { useEventCallback } from "usehooks-ts"
@ -65,12 +67,12 @@ export const SubscriptionList = ({
const data = useMemo(
() => [
"Starred",
"Lists",
"words.starred",
"words.lists",
...sortedListIds,
"Inbox",
"words.inbox",
...inboxes,
"Feeds",
"words.feeds",
...sortedGrouped,
...sortedUnGrouped,
],
@ -138,26 +140,26 @@ const ItemRender = ({
}) => {
if (typeof item === "string") {
switch (item) {
case "Starred": {
case "words.starred": {
return <StarItem />
}
case "Inbox": {
case "words.inbox": {
if (!extraData) return null
const { inboxIndexRange } = extraData
if (inboxIndexRange[0] > inboxIndexRange[1]) return null
return <SectionTitle title={item} />
return <SectionTitle transKey={item} />
}
case "Lists": {
case "words.lists": {
if (!extraData) return null
const { listsIndexRange } = extraData
if (listsIndexRange[0] > listsIndexRange[1]) return null
return <SectionTitle title={item} />
return <SectionTitle transKey={item} />
}
case "Feeds": {
case "words.feeds": {
if (!extraData) return null
const { feedsIndexRange } = extraData
if (feedsIndexRange[0] > feedsIndexRange[1]) return null
return <SectionTitle title={item} />
return <SectionTitle transKey={item} />
}
default: {
if (!extraData) return null
@ -203,7 +205,8 @@ const ItemRender = ({
)
}
const SectionTitle = ({ title }: { title: string }) => {
const SectionTitle = ({ transKey }: { transKey: ParseKeys<"common"> }) => {
const { t } = useTranslation("common")
return (
<View
style={{
@ -214,7 +217,7 @@ const SectionTitle = ({ title }: { title: string }) => {
}}
>
<Text className="text-secondary-label" ellipsizeMode="tail" numberOfLines={1}>
{title}
{t(transKey)}
</Text>
</View>
)
@ -222,6 +225,7 @@ const SectionTitle = ({ title }: { title: string }) => {
const StarItem = () => {
const navigation = useNavigation()
const { t } = useTranslation("common")
return (
<GroupedInsetListCard showSeparator={false} className="mt-4">
@ -242,7 +246,7 @@ const StarItem = () => {
>
<StarCuteFiIcon color="rgb(245, 158, 11)" height={20} width={20} />
<Text className="text-text ml-2 font-medium" style={{ marginLeft: GROUPED_ICON_TEXT_GAP }}>
Starred
{t("words.starred")}
</Text>
</ItemPressable>
</GroupedInsetListCard>

View File

@ -1,5 +1,6 @@
import type { FeedViewType } from "@follow/constants"
import { Fragment, useCallback, useEffect, useMemo } from "react"
import { useTranslation } from "react-i18next"
import {
ActivityIndicator,
FlatList,
@ -62,6 +63,7 @@ export const ProfileScreen: NavigationControllerView<{
}
function ProfileScreenImpl(props: { userId: string }) {
const { t } = useTranslation()
const scrollY = useSharedValue(0)
const {
data: subscriptions,
@ -107,7 +109,9 @@ function ProfileScreenImpl(props: { userId: string }) {
>
<BlurEffect />
<InternalNavigationHeader
title={`${user?.name}'s Profile`}
title={t("profile.title", {
name: user?.name,
})}
headerRight={
<UINavigationHeaderActionButton onPress={openShareUrl}>
<Share3CuteReIcon color={textLabelColor} />
@ -152,6 +156,8 @@ type PickedFeedModel = Pick<
view: FeedViewType
}
const SubscriptionList = ({ subscriptions }: { subscriptions: Subscription[] }) => {
const { t: tCommon } = useTranslation("common")
const { t } = useTranslation()
const { lists, feeds, groupedFeeds } = useMemo(() => {
const lists = [] as PickedListModel[]
const feeds = [] as PickedFeedModel[]
@ -201,7 +207,7 @@ const SubscriptionList = ({ subscriptions }: { subscriptions: Subscription[] })
<View>
{lists.length > 0 && (
<Fragment>
<SectionHeader title="Lists" />
<SectionHeader title={tCommon("words.lists")} />
<GroupedInsetListCard>
<FlatList
@ -215,7 +221,7 @@ const SubscriptionList = ({ subscriptions }: { subscriptions: Subscription[] })
)}
{hasFeeds && (
<View className="mt-4">
<SectionHeader title="Feeds" />
<SectionHeader title={tCommon("words.feeds")} />
{Object.entries(groupedFeeds).map(([category, feeds]) => (
<Fragment key={category}>
<GroupedInsetListSectionHeader label={category} marginSize="small" />
@ -230,7 +236,10 @@ const SubscriptionList = ({ subscriptions }: { subscriptions: Subscription[] })
</Fragment>
))}
<GroupedInsetListSectionHeader label="Uncategorized Feeds" marginSize="small" />
<GroupedInsetListSectionHeader
label={t("profile.uncategorized_feeds")}
marginSize="small"
/>
<GroupedInsetListCard>
<FlatList
scrollEnabled={false}

View File

@ -1,4 +1,5 @@
import { useContext } from "react"
import { useTranslation } from "react-i18next"
import type { ScrollView } from "react-native"
import { Text, TouchableOpacity, View } from "react-native"
import type { SharedValue } from "react-native-reanimated"
@ -73,6 +74,7 @@ const SettingHeader = ({ scrollY }: { scrollY: SharedValue<number> }) => {
}
const EditProfileButton = () => {
const { t } = useTranslation("common")
const navigation = useNavigation()
return (
<TouchableOpacity
@ -81,7 +83,7 @@ const EditProfileButton = () => {
onPress={() => navigation.pushControllerView(EditProfileScreen)}
>
<BlurEffect />
<Text className="text-label text-sm font-medium">Edit</Text>
<Text className="text-label text-sm font-medium">{t("words.edit")}</Text>
</TouchableOpacity>
)
}

View File

@ -96,12 +96,6 @@
"feed_item.claimed_feed": "خلاصة مطالبة",
"feed_item.error_since": "خطأ منذ",
"feed_item.not_publicly_visible": "غير مرئية بشكل عام في صفحة ملفك الشخصي",
"feed_view_type.articles": "مقالات",
"feed_view_type.audios": "ملفات صوتية",
"feed_view_type.notifications": "إشعارات",
"feed_view_type.pictures": "صور",
"feed_view_type.social_media": "وسائل التواصل الاجتماعي",
"feed_view_type.videos": "فيديوهات",
"mark_all_read_button.auto_confirm_info": "سيتم التأكيد تلقائيًا بعد {{countdown}} ثوانٍ.",
"mark_all_read_button.confirm": "تأكيد",
"mark_all_read_button.confirm_mark_all": "هل تريد تحديد <which /> كمقروء؟",

View File

@ -100,12 +100,6 @@
"feed_item.claimed_feed": "خلاصة مدعاة",
"feed_item.error_since": "خطأ منذ",
"feed_item.not_publicly_visible": "غير مرئي علنًا في صفحة ملفك الشخصي",
"feed_view_type.articles": "مقالات",
"feed_view_type.audios": "صوتيات",
"feed_view_type.notifications": "إشعارات",
"feed_view_type.pictures": "صور",
"feed_view_type.social_media": "وسائل التواصل الاجتماعي",
"feed_view_type.videos": "فيديوهات",
"mark_all_read_button.auto_confirm_info": "سيتم التأكيد تلقائيًا بعد {{countdown}} ثوانٍ.",
"mark_all_read_button.confirm": "تأكيد",
"mark_all_read_button.confirm_mark_all": "تحديد <which /> كمقروء؟",

View File

@ -100,12 +100,6 @@
"feed_item.claimed_feed": "تمت المطالبة بالتغذية",
"feed_item.error_since": "خطأ منذ",
"feed_item.not_publicly_visible": "غير مرئي للجمهور في صفحة ملفك الشخصي",
"feed_view_type.articles": "مقالات",
"feed_view_type.audios": "مقاطع صوتية",
"feed_view_type.notifications": "إشعارات",
"feed_view_type.pictures": "صور",
"feed_view_type.social_media": "وسائل التواصل الاجتماعي",
"feed_view_type.videos": "فيديوهات",
"mark_all_read_button.auto_confirm_info": "سيتم التأكيد تلقائيًا بعد {{countdown}} ثوانٍ.",
"mark_all_read_button.confirm": "تأكيد",
"mark_all_read_button.confirm_mark_all": "وضع علامة <which /> كمقروء؟",

View File

@ -96,12 +96,6 @@
"feed_item.claimed_feed": "الموجز المُطالب به",
"feed_item.error_since": "خطأ منذ",
"feed_item.not_publicly_visible": "غير مرئي علنًا في صفحة ملفك الشخصي",
"feed_view_type.articles": "مقالات",
"feed_view_type.audios": "ملفات صوتية",
"feed_view_type.notifications": "إشعارات",
"feed_view_type.pictures": "صور",
"feed_view_type.social_media": "وسائل التواصل الاجتماعي",
"feed_view_type.videos": "فيديوهات",
"mark_all_read_button.auto_confirm_info": "سيتم التأكيد تلقائيًا بعد {{countdown}} ثوانٍ.",
"mark_all_read_button.confirm": "تأكيد",
"mark_all_read_button.confirm_mark_all": "وضع علامة على <which /> كمقروء؟",

View File

@ -93,12 +93,6 @@
"feed_item.claimed_feed": "خلاصة مطالبة",
"feed_item.error_since": "خطأ منذ",
"feed_item.not_publicly_visible": "غير مرئي بشكل عام في صفحة ملفك الشخصي",
"feed_view_type.articles": "مقالات",
"feed_view_type.audios": "ملفات صوتية",
"feed_view_type.notifications": "إشعارات",
"feed_view_type.pictures": "صور",
"feed_view_type.social_media": "وسائل التواصل الاجتماعي",
"feed_view_type.videos": "فيديوهات",
"mark_all_read_button.auto_confirm_info": "سيتم التأكيد تلقائيًا بعد {{countdown}} ثوانٍ.",
"mark_all_read_button.confirm": "تأكيد",
"mark_all_read_button.confirm_mark_all": "تمييز <which /> كمقروء؟",

View File

@ -97,12 +97,6 @@
"feed_item.claimed_feed": "تغذية مُدعاة",
"feed_item.error_since": "خطأ منذ",
"feed_item.not_publicly_visible": "غير مرئية علنًا في صفحة ملفك الشخصي",
"feed_view_type.articles": "مقالات",
"feed_view_type.audios": "مقاطع صوتية",
"feed_view_type.notifications": "إشعارات",
"feed_view_type.pictures": "صور",
"feed_view_type.social_media": "وسائل التواصل الاجتماعي",
"feed_view_type.videos": "مقاطع فيديو",
"mark_all_read_button.auto_confirm_info": "سيتم التأكيد تلقائيًا بعد {{countdown}} ثوانٍ.",
"mark_all_read_button.confirm": "تأكيد",
"mark_all_read_button.confirm_mark_all": "وضع علامة <which /> كمقروء؟",

View File

@ -100,12 +100,6 @@
"feed_item.claimed_feed": "Beanspruchter Feed",
"feed_item.error_since": "Fehler seit",
"feed_item.not_publicly_visible": "Nicht öffentlich auf Ihrer Profilseite sichtbar",
"feed_view_type.articles": "Artikel",
"feed_view_type.audios": "Audios",
"feed_view_type.notifications": "Benachrichtigungen",
"feed_view_type.pictures": "Bilder",
"feed_view_type.social_media": "Soziale Medien",
"feed_view_type.videos": "Videos",
"mark_all_read_button.auto_confirm_info": "Wird nach {{countdown}} Sekunden automatisch bestätigt.",
"mark_all_read_button.confirm": "Bestätigen",
"mark_all_read_button.confirm_mark_all": "<which /> als gelesen markieren?",

View File

@ -39,29 +39,6 @@
"boost.feed_being_boosted": "Feed being boosted",
"boost.remaining_boosts_to_level_up": "{{remainingBoostsToLevelUp}} more boost will unlock the next level of benefits!",
"discover.any_url_or_keyword": "Any URL or Keyword",
"discover.category.all": "All",
"discover.category.anime": "ACG",
"discover.category.bbs": "BBS",
"discover.category.blog": "Blog",
"discover.category.design": "Design",
"discover.category.finance": "Finance",
"discover.category.forecast": "Forecast",
"discover.category.game": "Gaming",
"discover.category.government": "Government",
"discover.category.journal": "Scientific Journal",
"discover.category.live": "Live",
"discover.category.multimedia": "Multimedia",
"discover.category.new-media": "New Media",
"discover.category.picture": "Picture",
"discover.category.program-update": "Application Updates",
"discover.category.programming": "Programming",
"discover.category.reading": "Reading",
"discover.category.shopping": "Shopping",
"discover.category.social-media": "Social Media",
"discover.category.study": "Study",
"discover.category.traditional-media": "News",
"discover.category.travel": "Travel",
"discover.category.university": "University",
"discover.default_option": " (default)",
"discover.feed_description": "The description of this feed is as follows, and you can fill out the parameter form with the relevant information.",
"discover.feed_maintainers": "This feed is provided by RSSHub, with credit to <maintainers />",
@ -228,12 +205,6 @@
"feed_item.claimed_list": "Claimed List",
"feed_item.error_since": "Error since",
"feed_item.not_publicly_visible": "Not publicly visible on your profile page",
"feed_view_type.articles": "Articles",
"feed_view_type.audios": "Audios",
"feed_view_type.notifications": "Notifications",
"feed_view_type.pictures": "Pictures",
"feed_view_type.social_media": "Social Media",
"feed_view_type.videos": "Videos",
"login.confirm_password.label": "Confirm Password",
"login.continueWith": "Continue with {{provider}}",
"login.email": "Email",

View File

@ -64,12 +64,6 @@
"feed_form.update_follow": "Actualizar seguimiento",
"feed_form.updated": "🎉 Actualizado.",
"feed_form.view": "Vista",
"feed_view_type.articles": "Artículos",
"feed_view_type.audios": "Audios",
"feed_view_type.notifications": "Notificaciones",
"feed_view_type.pictures": "Imágenes",
"feed_view_type.social_media": "Redes Sociales",
"feed_view_type.videos": "Videos",
"mark_all_read_button.auto_confirm_info": "Confirmación automática después de {{countdown}} segundos.",
"mark_all_read_button.confirm": "Confirmar",
"mark_all_read_button.confirm_mark_all": "¿Marcar <which /> como leído?",

View File

@ -93,12 +93,6 @@
"feed_item.claimed_feed": "Vahvistettu syöte",
"feed_item.error_since": "Virhe alkaen",
"feed_item.not_publicly_visible": "Ei julkisesti näkyvissä profiilisivullasi",
"feed_view_type.articles": "Artikkelit",
"feed_view_type.audios": "Äänitteet",
"feed_view_type.notifications": "Ilmoitukset",
"feed_view_type.pictures": "Kuvat",
"feed_view_type.social_media": "Sosiaalinen media",
"feed_view_type.videos": "Videot",
"mark_all_read_button.auto_confirm_info": "Vahvistetaan automaattisesti {{countdown}} sekunnin kuluttua.",
"mark_all_read_button.confirm": "Vahvista",
"mark_all_read_button.confirm_mark_all": "Merkitäänkö <which /> luetuksi?",

View File

@ -93,12 +93,6 @@
"feed_item.claimed_feed": "Flux Réclamé",
"feed_item.error_since": "Erreur depuis",
"feed_item.not_publicly_visible": "Non visible publiquement sur votre page de profil",
"feed_view_type.articles": "Articles",
"feed_view_type.audios": "Audios",
"feed_view_type.notifications": "Notifications",
"feed_view_type.pictures": "Images",
"feed_view_type.social_media": "Réseaux sociaux",
"feed_view_type.videos": "Vidéos",
"mark_all_read_button.auto_confirm_info": "Sera confirmé automatiquement après {{countdown}}s.",
"mark_all_read_button.confirm": "Confirmer",
"mark_all_read_button.confirm_mark_all": "Marquer <which /> comme lu ?",

View File

@ -93,12 +93,6 @@
"feed_item.claimed_feed": "Feed Reclamato",
"feed_item.error_since": "Errore da",
"feed_item.not_publicly_visible": "Non visibile pubblicamente sulla tua pagina profilo",
"feed_view_type.articles": "Articoli",
"feed_view_type.audios": "Audio",
"feed_view_type.notifications": "Notifiche",
"feed_view_type.pictures": "Immagini",
"feed_view_type.social_media": "Social Media",
"feed_view_type.videos": "Video",
"mark_all_read_button.auto_confirm_info": "Sarà confermato automaticamente dopo {{countdown}}s.",
"mark_all_read_button.confirm": "Conferma",
"mark_all_read_button.confirm_mark_all": "Segna <which /> come letto?",

View File

@ -39,29 +39,6 @@
"boost.feed_being_boosted": "フィードをブーストしています",
"boost.remaining_boosts_to_level_up": "{{remainingBoostsToLevelUp}} さらなるレベルのブースト アンロックは特典として提供されます!",
"discover.any_url_or_keyword": "任意の URL またはキーワード",
"discover.category.all": "すべて",
"discover.category.anime": "ACG",
"discover.category.bbs": "BBS",
"discover.category.blog": "ブログ",
"discover.category.design": "デザイン",
"discover.category.finance": "金融",
"discover.category.forecast": "予報",
"discover.category.game": "ゲーム",
"discover.category.government": "政府",
"discover.category.journal": "科学/学術",
"discover.category.live": "Live",
"discover.category.multimedia": "マルチメディア",
"discover.category.new-media": "新メディア",
"discover.category.picture": "ピクチャー",
"discover.category.program-update": "アプリ",
"discover.category.programming": "プログラミング",
"discover.category.reading": "読みもの",
"discover.category.shopping": "買い物",
"discover.category.social-media": "ソーシャルメディア",
"discover.category.study": "学習",
"discover.category.traditional-media": "ニュース",
"discover.category.travel": "旅行",
"discover.category.university": "大学",
"discover.default_option": " (デフォルト)",
"discover.feed_description": "このフィードの説明は Folo で表示されます、またパラメータ フォームに関連する情報を記入することができます。",
"discover.feed_maintainers": "フィードは RSSHub が提供し <maintainers /> とともにクレジットされます。",
@ -226,12 +203,6 @@
"feed_item.claimed_list": "クレームされたリスト",
"feed_item.error_since": "エラー発生時刻",
"feed_item.not_publicly_visible": "プロフィールページに公開されていません",
"feed_view_type.articles": "記事",
"feed_view_type.audios": "オーディオ",
"feed_view_type.notifications": "通知",
"feed_view_type.pictures": "画像",
"feed_view_type.social_media": "ソーシャルメディア",
"feed_view_type.videos": "動画",
"login.confirm_password.label": "パスワードの確認",
"login.continueWith": "{{provider}} で続ける",
"login.email": "Email",

View File

@ -39,29 +39,6 @@
"boost.feed_being_boosted": "피드가 부스트 중",
"boost.remaining_boosts_to_level_up": "{{remainingBoostsToLevelUp}} 더 많은 부스트를 하면 다음 단계의 혜택이 잠금 해제됩니다!",
"discover.any_url_or_keyword": "임의의 URL 또는 키워드",
"discover.category.all": "전체",
"discover.category.anime": "ACG",
"discover.category.bbs": "BBS",
"discover.category.blog": "블로그",
"discover.category.design": "디자인",
"discover.category.finance": "금융",
"discover.category.forecast": "예측",
"discover.category.game": "게임",
"discover.category.government": "정부",
"discover.category.journal": "과학 저널",
"discover.category.live": "라이브",
"discover.category.multimedia": "멀티미디어",
"discover.category.new-media": "뉴미디어",
"discover.category.picture": "사진",
"discover.category.program-update": "애플리케이션 업데이트",
"discover.category.programming": "프로그래밍",
"discover.category.reading": "독서",
"discover.category.shopping": "쇼핑",
"discover.category.social-media": "소셜 미디어",
"discover.category.study": "학습",
"discover.category.traditional-media": "뉴스",
"discover.category.travel": "여행",
"discover.category.university": "대학교",
"discover.default_option": " (기본값)",
"discover.feed_description": "이 피드의 설명은 다음과 같으며, 관련 정보를 사용하여 매개변수 양식을 작성할 수 있습니다.",
"discover.feed_maintainers": "이 피드는 RSSHub에서 제공하며, <maintainers />에게 크레딧을 제공합니다.",
@ -226,12 +203,6 @@
"feed_item.claimed_list": "클레임된 목록",
"feed_item.error_since": "오류 발생:",
"feed_item.not_publicly_visible": "프로필 페이지에 공개적으로 표시되지 않음",
"feed_view_type.articles": "기사",
"feed_view_type.audios": "오디오",
"feed_view_type.notifications": "알림",
"feed_view_type.pictures": "사진",
"feed_view_type.social_media": "소셜 미디어",
"feed_view_type.videos": "비디오",
"login.confirm_password.label": "비밀번호 확인",
"login.continueWith": "{{provider}}로 계속하기",
"login.email": "이메일",

View File

@ -93,12 +93,6 @@
"feed_item.claimed_feed": "クレームされたフィード",
"feed_item.error_since": "エラー発生時刻",
"feed_item.not_publicly_visible": "プロフィールページに公開されていません",
"feed_view_type.articles": "記事",
"feed_view_type.audios": "オーディオ",
"feed_view_type.notifications": "通知",
"feed_view_type.pictures": "画像",
"feed_view_type.social_media": "ソーシャルメディア",
"feed_view_type.videos": "動画",
"mark_all_read_button.auto_confirm_info": "{{countdown}} 秒後に自動的に確認されます。",
"mark_all_read_button.confirm": "確認",
"mark_all_read_button.confirm_mark_all": "<which />を既読にしますか?",

View File

@ -39,29 +39,6 @@
"boost.feed_being_boosted": "Канал в процессе усиления",
"boost.remaining_boosts_to_level_up": "{{remainingBoostsToLevelUp}} усилений для разблокировки следующего уровня преимуществ!",
"discover.any_url_or_keyword": "Любой URL или ключевое слово",
"discover.category.all": "Все",
"discover.category.anime": "Аниме",
"discover.category.bbs": "BBS",
"discover.category.blog": "Блог",
"discover.category.design": "Дизайн",
"discover.category.finance": "Финансы",
"discover.category.forecast": "Прогнозы",
"discover.category.game": "Игры",
"discover.category.government": "Государственные",
"discover.category.journal": "Научный журнал",
"discover.category.live": "Прямой эфир",
"discover.category.multimedia": "Мультимедиа",
"discover.category.new-media": "Новые медиа",
"discover.category.picture": "Изображения",
"discover.category.program-update": "Обновления программы",
"discover.category.programming": "Программирование",
"discover.category.reading": "Чтение",
"discover.category.shopping": "Шоппинг",
"discover.category.social-media": "Социальные сети",
"discover.category.study": "Учёба",
"discover.category.traditional-media": "Новости",
"discover.category.travel": "Путешествия",
"discover.category.university": "Университет",
"discover.default_option": " (по умолчанию)",
"discover.feed_description": "Описание этого канала следующее, и вы можете заполнить форму параметров с соответствующей информацией.",
"discover.feed_maintainers": "Этот канал предоставлен RSSHub, с благодарностью <maintainers />",
@ -222,12 +199,6 @@
"feed_item.claimed_list": "Заявленный список",
"feed_item.error_since": "Ошибка с",
"feed_item.not_publicly_visible": "Не виден на вашей публичной странице профиля",
"feed_view_type.articles": "Статьи",
"feed_view_type.audios": "Аудио",
"feed_view_type.notifications": "Уведомления",
"feed_view_type.pictures": "Картинки",
"feed_view_type.social_media": "Социальные сети",
"feed_view_type.videos": "Видео",
"login.confirm_password.label": "Подтвердите пароль",
"login.continueWith": "Продолжить с {{provider}}",
"login.email": "Электронная почта",

View File

@ -114,12 +114,6 @@
"feed_item.claimed_feed": "Talep Edilen Besleme",
"feed_item.error_since": "Hata başlangıcı",
"feed_item.not_publicly_visible": "Profil sayfanızda herkese açık olarak görünmüyor",
"feed_view_type.articles": "Makaleler",
"feed_view_type.audios": "Sesler",
"feed_view_type.notifications": "Bildirimler",
"feed_view_type.pictures": "Resimler",
"feed_view_type.social_media": "Sosyal Medya",
"feed_view_type.videos": "Videolar",
"mark_all_read_button.auto_confirm_info": "{{countdown}} saniye sonra otomatik olarak onaylanacak.",
"mark_all_read_button.confirm": "Onayla",
"mark_all_read_button.confirm_mark_all": "<which /> okundu olarak işaretlensin mi?",

View File

@ -39,29 +39,6 @@
"boost.feed_being_boosted": "已助力",
"boost.remaining_boosts_to_level_up": "再助力 {{remainingBoostsToLevelUp}} 次即可解锁下一级福利!",
"discover.any_url_or_keyword": "任意链接或关键词",
"discover.category.all": "全部",
"discover.category.anime": "二次元",
"discover.category.bbs": "论坛",
"discover.category.blog": "博客",
"discover.category.design": "设计",
"discover.category.finance": "金融",
"discover.category.forecast": "预报预警",
"discover.category.game": "游戏",
"discover.category.government": "政务消息",
"discover.category.journal": "科学期刊",
"discover.category.live": "直播",
"discover.category.multimedia": "音视频",
"discover.category.new-media": "新媒体",
"discover.category.picture": "图片",
"discover.category.program-update": "程序更新",
"discover.category.programming": "编程",
"discover.category.reading": "阅读",
"discover.category.shopping": "购物",
"discover.category.social-media": "社交媒体",
"discover.category.study": "学习",
"discover.category.traditional-media": "传统媒体",
"discover.category.travel": "出行旅游",
"discover.category.university": "大学通知",
"discover.default_option": "(默认)",
"discover.feed_description": "根据描述完善目标订阅源的相关信息。",
"discover.feed_maintainers": "由 RSSHub 提供,感谢贡献者 <maintainers />",
@ -228,12 +205,6 @@
"feed_item.claimed_list": "已认证列表",
"feed_item.error_since": "源失效:",
"feed_item.not_publicly_visible": "在个人页面上隐藏",
"feed_view_type.articles": "文章",
"feed_view_type.audios": "音频",
"feed_view_type.notifications": "通知",
"feed_view_type.pictures": "图片",
"feed_view_type.social_media": "社交媒体",
"feed_view_type.videos": "视频",
"login.confirm_password.label": "确认密码",
"login.continueWith": "使用 {{provider}} 登入",
"login.email": "邮件地址",

View File

@ -39,29 +39,6 @@
"boost.feed_being_boosted": "動態正在加成中",
"boost.remaining_boosts_to_level_up": "還差 {{remainingBoostsToLevelUp}} 次加成即可解鎖下一級優惠!",
"discover.any_url_or_keyword": "任何網址或關鍵字",
"discover.category.all": "全部",
"discover.category.anime": "ACG",
"discover.category.bbs": "討論區",
"discover.category.blog": "博客",
"discover.category.design": "設計",
"discover.category.finance": "財經",
"discover.category.forecast": "預測",
"discover.category.game": "遊戲",
"discover.category.government": "政府",
"discover.category.journal": "科學期刊",
"discover.category.live": "直播",
"discover.category.multimedia": "多媒體",
"discover.category.new-media": "新媒體",
"discover.category.picture": "圖片",
"discover.category.program-update": "應用程式更新",
"discover.category.programming": "程式設計",
"discover.category.reading": "閱讀",
"discover.category.shopping": "購物",
"discover.category.social-media": "社交媒體",
"discover.category.study": "學習",
"discover.category.traditional-media": "新聞",
"discover.category.travel": "旅遊",
"discover.category.university": "大學",
"discover.default_option": " (預設)",
"discover.feed_description": "此訂閱源的描述如下,你可以根據相關資訊填寫參數表格。",
"discover.feed_maintainers": "此訂閱源由 RSSHub 提供,感謝 <maintainers />",
@ -228,12 +205,6 @@
"feed_item.claimed_list": "已認領清單",
"feed_item.error_since": "錯誤始於",
"feed_item.not_publicly_visible": "在你的個人頁面上不公開可見",
"feed_view_type.articles": "文章",
"feed_view_type.audios": "音訊",
"feed_view_type.notifications": "通知",
"feed_view_type.pictures": "圖片",
"feed_view_type.social_media": "社交媒體",
"feed_view_type.videos": "影片",
"login.confirm_password.label": "確認密碼",
"login.continueWith": "繼續使用 {{provider}}",
"login.email": "電子郵件",

View File

@ -39,29 +39,6 @@
"boost.feed_being_boosted": "訂閱正在加成",
"boost.remaining_boosts_to_level_up": "再次加成 {{remainingBoostsToLevelUp}} 次即可解鎖下一級獎勵!",
"discover.any_url_or_keyword": "任何 URL 或關鍵字",
"discover.category.all": "全部",
"discover.category.anime": "動漫",
"discover.category.bbs": "論壇",
"discover.category.blog": "部落格",
"discover.category.design": "設計",
"discover.category.finance": "金融",
"discover.category.forecast": "預報預警",
"discover.category.game": "遊戲",
"discover.category.government": "政府資訊",
"discover.category.journal": "科學期刊",
"discover.category.live": "直播",
"discover.category.multimedia": "多媒體",
"discover.category.new-media": "新媒體",
"discover.category.picture": "圖片",
"discover.category.program-update": "程式更新",
"discover.category.programming": "程式設計",
"discover.category.reading": "閱讀",
"discover.category.shopping": "購物",
"discover.category.social-media": "社交媒體",
"discover.category.study": "學習",
"discover.category.traditional-media": "傳統媒體",
"discover.category.travel": "出行旅遊",
"discover.category.university": "大學資訊",
"discover.default_option": "(預設)",
"discover.feed_description": "根據描述完成目標 RSS 摘要的相關資訊。",
"discover.feed_maintainers": "由 RSSHub 提供,感謝 <maintainers /> 的支持",
@ -223,12 +200,6 @@
"feed_item.claimed_list": "已認領列表",
"feed_item.error_since": "RSS 摘要失效:",
"feed_item.not_publicly_visible": "在您的個人頁面上隱藏",
"feed_view_type.articles": "文章",
"feed_view_type.audios": "音訊",
"feed_view_type.notifications": "通知",
"feed_view_type.pictures": "圖片",
"feed_view_type.social_media": "社交媒體",
"feed_view_type.videos": "影片",
"login.confirm_password.label": "確認密碼",
"login.continueWith": "透過 {{provider}} 登入",
"login.email": "電子信箱",

View File

@ -3,6 +3,12 @@
"cancel": "إلغاء",
"close": "إغلاق",
"confirm": "تأكيد",
"feed_view_type.articles": "مقالات",
"feed_view_type.audios": "ملفات صوتية",
"feed_view_type.notifications": "إشعارات",
"feed_view_type.pictures": "صور",
"feed_view_type.social_media": "وسائل التواصل الاجتماعي",
"feed_view_type.videos": "فيديوهات",
"ok": "موافق",
"quantifier.piece": "",
"space": " ",
@ -25,5 +31,7 @@
"words.result": "نتيجة",
"words.result_one": "نتيجة",
"words.result_other": "نتائج",
"words.search": "بحث",
"words.starred": "مميزة بنجمة",
"words.which.all": "الكل"
}

View File

@ -2,6 +2,12 @@
"app.copied_to_clipboard": "تم النسخ إلى الحافظة",
"cancel": "إلغاء",
"confirm": "تأكيد",
"feed_view_type.articles": "مقالات",
"feed_view_type.audios": "صوتيات",
"feed_view_type.notifications": "إشعارات",
"feed_view_type.pictures": "صور",
"feed_view_type.social_media": "وسائل التواصل الاجتماعي",
"feed_view_type.videos": "فيديوهات",
"ok": "موافق",
"quantifier.piece": "",
"space": " ",
@ -24,5 +30,7 @@
"words.result": "نتيجة",
"words.result_one": "نتيجة",
"words.result_other": "نتائج",
"words.search": "بحث",
"words.starred": "المفضلة",
"words.which.all": "الكل"
}

View File

@ -2,6 +2,12 @@
"app.copied_to_clipboard": "تم النسخ إلى الحافظة",
"cancel": "إلغاء",
"confirm": "تأكيد",
"feed_view_type.articles": "مقالات",
"feed_view_type.audios": "مقاطع صوتية",
"feed_view_type.notifications": "إشعارات",
"feed_view_type.pictures": "صور",
"feed_view_type.social_media": "وسائل التواصل الاجتماعي",
"feed_view_type.videos": "فيديوهات",
"ok": "موافق",
"quantifier.piece": "",
"space": " ",
@ -24,5 +30,7 @@
"words.result": "نتيجة",
"words.result_one": "نتيجة",
"words.result_other": "نتائج",
"words.search": "بحث",
"words.starred": "مفضلة",
"words.which.all": "الكل"
}

View File

@ -3,6 +3,12 @@
"cancel": "إلغاء",
"close": "إغلاق",
"confirm": "تأكيد",
"feed_view_type.articles": "مقالات",
"feed_view_type.audios": "ملفات صوتية",
"feed_view_type.notifications": "إشعارات",
"feed_view_type.pictures": "صور",
"feed_view_type.social_media": "وسائل التواصل الاجتماعي",
"feed_view_type.videos": "فيديوهات",
"ok": "موافق",
"quantifier.piece": "",
"space": " ",
@ -25,5 +31,7 @@
"words.result": "نتيجة",
"words.result_one": "نتيجة",
"words.result_other": "نتائج",
"words.search": "بحث",
"words.starred": "المفضلة",
"words.which.all": "الكل"
}

View File

@ -3,6 +3,12 @@
"cancel": "إلغاء",
"close": "إغلاق",
"confirm": "تأكيد",
"feed_view_type.articles": "مقالات",
"feed_view_type.audios": "ملفات صوتية",
"feed_view_type.notifications": "إشعارات",
"feed_view_type.pictures": "صور",
"feed_view_type.social_media": "وسائل التواصل الاجتماعي",
"feed_view_type.videos": "فيديوهات",
"ok": "حسنًا",
"quantifier.piece": "",
"space": " ",
@ -25,5 +31,7 @@
"words.result": "نتيجة",
"words.result_one": "نتيجة",
"words.result_other": "نتائج",
"words.search": "بحث",
"words.starred": "مميز",
"words.which.all": "الكل"
}

View File

@ -2,6 +2,12 @@
"app.copied_to_clipboard": "تم النسخ إلى الحافظة",
"cancel": "إلغاء",
"confirm": "تأكيد",
"feed_view_type.articles": "مقالات",
"feed_view_type.audios": "مقاطع صوتية",
"feed_view_type.notifications": "إشعارات",
"feed_view_type.pictures": "صور",
"feed_view_type.social_media": "وسائل التواصل الاجتماعي",
"feed_view_type.videos": "مقاطع فيديو",
"ok": "موافق",
"quantifier.piece": "",
"space": " ",
@ -24,5 +30,7 @@
"words.result": "نتيجة",
"words.result_one": "نتيجة",
"words.result_other": "نتائج",
"words.search": "بحث",
"words.starred": "المفضلة",
"words.which.all": "الكل"
}

View File

@ -3,6 +3,12 @@
"cancel": "Abbrechen",
"close": "Schließen",
"confirm": "Bestätigen",
"feed_view_type.articles": "Artikel",
"feed_view_type.audios": "Audios",
"feed_view_type.notifications": "Benachrichtigungen",
"feed_view_type.pictures": "Bilder",
"feed_view_type.social_media": "Soziale Medien",
"feed_view_type.videos": "Videos",
"ok": "OK",
"quantifier.piece": "",
"space": " ",
@ -25,5 +31,7 @@
"words.result": "Ergebnis",
"words.result_one": "Ergebnis",
"words.result_other": "Ergebnisse",
"words.search": "Suche",
"words.starred": "Favorisiert",
"words.which.all": "Alle"
}

View File

@ -3,6 +3,35 @@
"cancel": "Cancel",
"close": "Close",
"confirm": "Confirm",
"discover.category.all": "All",
"discover.category.anime": "ACG",
"discover.category.bbs": "BBS",
"discover.category.blog": "Blog",
"discover.category.design": "Design",
"discover.category.finance": "Finance",
"discover.category.forecast": "Forecast",
"discover.category.game": "Gaming",
"discover.category.government": "Government",
"discover.category.journal": "Scientific Journal",
"discover.category.live": "Live",
"discover.category.multimedia": "Multimedia",
"discover.category.new-media": "New Media",
"discover.category.picture": "Picture",
"discover.category.program-update": "Application Updates",
"discover.category.programming": "Programming",
"discover.category.reading": "Reading",
"discover.category.shopping": "Shopping",
"discover.category.social-media": "Social Media",
"discover.category.study": "Study",
"discover.category.traditional-media": "News",
"discover.category.travel": "Travel",
"discover.category.university": "University",
"feed_view_type.articles": "Articles",
"feed_view_type.audios": "Audios",
"feed_view_type.notifications": "Notifications",
"feed_view_type.pictures": "Pictures",
"feed_view_type.social_media": "Social Media",
"feed_view_type.videos": "Videos",
"ok": "OK",
"quantifier.piece": "",
"retry": "Retry",
@ -24,10 +53,13 @@
"words.edit": "Edit",
"words.entry": "Entry",
"words.expand": "Expand",
"words.feeds": "Feeds",
"words.follow": "Follow",
"words.id": "ID",
"words.inbox": "Inbox",
"words.items_one": "Item",
"words.items_other": "Items",
"words.lists": "Lists",
"words.local": "local",
"words.manage": "Manage",
"words.record": "record",
@ -39,6 +71,8 @@
"words.result_other": "results",
"words.rsshub": "RSSHub",
"words.save": "Save",
"words.search": "Search",
"words.starred": "Starred",
"words.submit": "Submit",
"words.update": "Update",
"words.which.all": "All"

View File

@ -3,6 +3,12 @@
"cancel": "Cancelar",
"close": "Cerrar",
"confirm": "Confirmar",
"feed_view_type.articles": "Artículos",
"feed_view_type.audios": "Audios",
"feed_view_type.notifications": "Notificaciones",
"feed_view_type.pictures": "Imágenes",
"feed_view_type.social_media": "Redes Sociales",
"feed_view_type.videos": "Videos",
"ok": "OK",
"quantifier.piece": "",
"space": " ",
@ -25,5 +31,7 @@
"words.result": "resultado",
"words.result_one": "resultado",
"words.result_other": "resultados",
"words.search": "Buscar",
"words.starred": "Favoritos",
"words.which.all": "Todo"
}

View File

@ -2,6 +2,12 @@
"app.copied_to_clipboard": "Kopioitu leikepöydälle",
"cancel": "Peruuta",
"confirm": "Vahvista",
"feed_view_type.articles": "Artikkelit",
"feed_view_type.audios": "Äänitteet",
"feed_view_type.notifications": "Ilmoitukset",
"feed_view_type.pictures": "Kuvat",
"feed_view_type.social_media": "Sosiaalinen media",
"feed_view_type.videos": "Videot",
"ok": "OK",
"quantifier.piece": "",
"space": " ",
@ -24,5 +30,7 @@
"words.result": "tulos",
"words.result_one": "tulos",
"words.result_other": "tulokset",
"words.search": "Hae",
"words.starred": "Tähdät",
"words.which.all": "Kaikki"
}

View File

@ -3,6 +3,12 @@
"cancel": "Annuler",
"close": "Fermer",
"confirm": "Confirmer",
"feed_view_type.articles": "Articles",
"feed_view_type.audios": "Audios",
"feed_view_type.notifications": "Notifications",
"feed_view_type.pictures": "Images",
"feed_view_type.social_media": "Réseaux sociaux",
"feed_view_type.videos": "Vidéos",
"ok": "OK",
"quantifier.piece": "",
"space": " ",
@ -25,5 +31,7 @@
"words.result": "résultat",
"words.result_one": "résultat",
"words.result_other": "résultats",
"words.search": "Rechercher",
"words.starred": "Favoris",
"words.which.all": "Tous"
}

View File

@ -2,6 +2,12 @@
"app.copied_to_clipboard": "Copiato negli appunti",
"cancel": "Annulla",
"confirm": "Conferma",
"feed_view_type.articles": "Articoli",
"feed_view_type.audios": "Audio",
"feed_view_type.notifications": "Notifiche",
"feed_view_type.pictures": "Immagini",
"feed_view_type.social_media": "Social Media",
"feed_view_type.videos": "Video",
"ok": "OK",
"quantifier.piece": "",
"space": " ",
@ -24,5 +30,7 @@
"words.result": "risultato",
"words.result_one": "risultato",
"words.result_other": "risultati",
"words.search": "Cerca",
"words.starred": "Preferiti",
"words.which.all": "Tutti"
}

View File

@ -3,6 +3,35 @@
"cancel": "キャンセル",
"close": "閉じる",
"confirm": "確認",
"discover.category.all": "すべて",
"discover.category.anime": "ACG",
"discover.category.bbs": "BBS",
"discover.category.blog": "ブログ",
"discover.category.design": "デザイン",
"discover.category.finance": "金融",
"discover.category.forecast": "予報",
"discover.category.game": "ゲーム",
"discover.category.government": "政府",
"discover.category.journal": "科学/学術",
"discover.category.live": "Live",
"discover.category.multimedia": "マルチメディア",
"discover.category.new-media": "新メディア",
"discover.category.picture": "ピクチャー",
"discover.category.program-update": "アプリ",
"discover.category.programming": "プログラミング",
"discover.category.reading": "読みもの",
"discover.category.shopping": "買い物",
"discover.category.social-media": "ソーシャルメディア",
"discover.category.study": "学習",
"discover.category.traditional-media": "ニュース",
"discover.category.travel": "旅行",
"discover.category.university": "大学",
"feed_view_type.articles": "記事",
"feed_view_type.audios": "オーディオ",
"feed_view_type.notifications": "通知",
"feed_view_type.pictures": "画像",
"feed_view_type.social_media": "ソーシャルメディア",
"feed_view_type.videos": "動画",
"ok": "OK",
"quantifier.piece": "個",
"retry": "再試行",
@ -23,10 +52,13 @@
"words.edit": "編集",
"words.entry": "エントリー",
"words.expand": "展開",
"words.feeds": "フィード",
"words.follow": "フォロー",
"words.id": "ID",
"words.inbox": "受信トレイ",
"words.items_one": "アイテム",
"words.items_other": "アイテム",
"words.lists": "リスト",
"words.local": "ローカル",
"words.manage": "マネージ",
"words.record": "記録",
@ -38,6 +70,8 @@
"words.result_other": "結果",
"words.rsshub": "RSSHub",
"words.save": "保存",
"words.search": "検索",
"words.starred": "スター付き",
"words.submit": "送信",
"words.update": "更新",
"words.which.all": "すべて"

View File

@ -3,6 +3,35 @@
"cancel": "취소",
"close": "닫기",
"confirm": "확인",
"discover.category.all": "전체",
"discover.category.anime": "ACG",
"discover.category.bbs": "BBS",
"discover.category.blog": "블로그",
"discover.category.design": "디자인",
"discover.category.finance": "금융",
"discover.category.forecast": "예측",
"discover.category.game": "게임",
"discover.category.government": "정부",
"discover.category.journal": "과학 저널",
"discover.category.live": "라이브",
"discover.category.multimedia": "멀티미디어",
"discover.category.new-media": "뉴미디어",
"discover.category.picture": "사진",
"discover.category.program-update": "애플리케이션 업데이트",
"discover.category.programming": "프로그래밍",
"discover.category.reading": "독서",
"discover.category.shopping": "쇼핑",
"discover.category.social-media": "소셜 미디어",
"discover.category.study": "학습",
"discover.category.traditional-media": "뉴스",
"discover.category.travel": "여행",
"discover.category.university": "대학교",
"feed_view_type.articles": "기사",
"feed_view_type.audios": "오디오",
"feed_view_type.notifications": "알림",
"feed_view_type.pictures": "사진",
"feed_view_type.social_media": "소셜 미디어",
"feed_view_type.videos": "비디오",
"ok": "확인",
"quantifier.piece": "",
"retry": "재시도",
@ -23,10 +52,13 @@
"words.edit": "편집",
"words.entry": "항목",
"words.expand": "펼치기",
"words.feeds": "피드",
"words.follow": "팔로우",
"words.id": "ID",
"words.inbox": "받은편지함",
"words.items_one": "항목",
"words.items_other": "항목들",
"words.lists": "목록",
"words.local": "로컬",
"words.manage": "관리",
"words.record": "기록",
@ -38,6 +70,8 @@
"words.result_other": "결과들",
"words.rsshub": "RSSHub",
"words.save": "저장",
"words.search": "검색",
"words.starred": "별표 표시됨",
"words.submit": "제출",
"words.update": "업데이트",
"words.which.all": "모두"

View File

@ -3,6 +3,12 @@
"cancel": "Cancelar",
"close": "Fechar",
"confirm": "Confirmar",
"feed_view_type.articles": "記事",
"feed_view_type.audios": "オーディオ",
"feed_view_type.notifications": "通知",
"feed_view_type.pictures": "画像",
"feed_view_type.social_media": "ソーシャルメディア",
"feed_view_type.videos": "動画",
"ok": "OK",
"quantifier.piece": "",
"space": " ",
@ -25,5 +31,7 @@
"words.result": "resultado",
"words.result_one": "resultado",
"words.result_other": "resultados",
"words.search": "検索",
"words.starred": "スター付き",
"words.which.all": "Todos"
}

View File

@ -3,6 +3,35 @@
"cancel": "Отменить",
"close": "Закрыть",
"confirm": "Подтвердить",
"discover.category.all": "Все",
"discover.category.anime": "Аниме",
"discover.category.bbs": "BBS",
"discover.category.blog": "Блог",
"discover.category.design": "Дизайн",
"discover.category.finance": "Финансы",
"discover.category.forecast": "Прогнозы",
"discover.category.game": "Игры",
"discover.category.government": "Государственные",
"discover.category.journal": "Научный журнал",
"discover.category.live": "Прямой эфир",
"discover.category.multimedia": "Мультимедиа",
"discover.category.new-media": "Новые медиа",
"discover.category.picture": "Изображения",
"discover.category.program-update": "Обновления программы",
"discover.category.programming": "Программирование",
"discover.category.reading": "Чтение",
"discover.category.shopping": "Шоппинг",
"discover.category.social-media": "Социальные сети",
"discover.category.study": "Учёба",
"discover.category.traditional-media": "Новости",
"discover.category.travel": "Путешествия",
"discover.category.university": "Университет",
"feed_view_type.articles": "Статьи",
"feed_view_type.audios": "Аудио",
"feed_view_type.notifications": "Уведомления",
"feed_view_type.pictures": "Картинки",
"feed_view_type.social_media": "Социальные сети",
"feed_view_type.videos": "Видео",
"ok": "ОК",
"quantifier.piece": "",
"retry": "Повторить попытку",
@ -23,10 +52,13 @@
"words.edit": "Редактировать",
"words.entry": "Запись",
"words.expand": "Расширить",
"words.feeds": "Каналы",
"words.follow": "Следовать",
"words.id": "ID",
"words.inbox": "Почтовый ящик",
"words.items_one": "Элемент",
"words.items_other": "Элементы",
"words.lists": "Списки",
"words.local": "локальный",
"words.manage": "Управлять",
"words.record": "запись",
@ -38,6 +70,8 @@
"words.result_other": "результаты",
"words.rsshub": "RSSHub",
"words.save": "Сохранить",
"words.search": "Поиск",
"words.starred": "Избранное",
"words.submit": "Отправить",
"words.update": "Обновить",
"words.which.all": "Все"

View File

@ -3,6 +3,12 @@
"cancel": "İptal",
"close": "Kapat",
"confirm": "Onayla",
"feed_view_type.articles": "Makaleler",
"feed_view_type.audios": "Sesler",
"feed_view_type.notifications": "Bildirimler",
"feed_view_type.pictures": "Resimler",
"feed_view_type.social_media": "Sosyal Medya",
"feed_view_type.videos": "Videolar",
"ok": "Tamam",
"quantifier.piece": "",
"space": " ",
@ -17,9 +23,11 @@
"words.create": "Oluştur",
"words.edit": "Düzenle",
"words.entry": "Giriş",
"words.feeds": "Beslemeler",
"words.id": "ID",
"words.items_one": "Öğe",
"words.items_other": "Öğeler",
"words.lists": "Listeler",
"words.local": "yerel",
"words.manage": "Yönet",
"words.record": "kayıt",
@ -28,6 +36,8 @@
"words.result": "sonuç",
"words.result_one": "sonuç",
"words.result_other": "sonuçlar",
"words.search": "Ara",
"words.starred": "Yıldızlı",
"words.submit": "Gönder",
"words.update": "Güncelle",
"words.which.all": "Tümü"

View File

@ -3,6 +3,35 @@
"cancel": "取消",
"close": "关闭",
"confirm": "确认",
"discover.category.all": "全部",
"discover.category.anime": "二次元",
"discover.category.bbs": "论坛",
"discover.category.blog": "博客",
"discover.category.design": "设计",
"discover.category.finance": "金融",
"discover.category.forecast": "预报预警",
"discover.category.game": "游戏",
"discover.category.government": "政务消息",
"discover.category.journal": "科学期刊",
"discover.category.live": "直播",
"discover.category.multimedia": "音视频",
"discover.category.new-media": "新媒体",
"discover.category.picture": "图片",
"discover.category.program-update": "程序更新",
"discover.category.programming": "编程",
"discover.category.reading": "阅读",
"discover.category.shopping": "购物",
"discover.category.social-media": "社交媒体",
"discover.category.study": "学习",
"discover.category.traditional-media": "传统媒体",
"discover.category.travel": "出行旅游",
"discover.category.university": "大学通知",
"feed_view_type.articles": "文章",
"feed_view_type.audios": "音频",
"feed_view_type.notifications": "通知",
"feed_view_type.pictures": "图片",
"feed_view_type.social_media": "社交媒体",
"feed_view_type.videos": "视频",
"ok": "好",
"quantifier.piece": "条",
"retry": "重试",
@ -24,10 +53,13 @@
"words.edit": "编辑",
"words.entry": "条目",
"words.expand": "展开",
"words.feeds": "订阅源",
"words.follow": "关注",
"words.id": "ID",
"words.inbox": "收件箱",
"words.items_one": "内容",
"words.items_other": "内容",
"words.lists": "列表",
"words.local": "本地",
"words.manage": "管理",
"words.record": "记录",
@ -39,6 +71,8 @@
"words.result_other": "结果",
"words.rsshub": "RSSHub",
"words.save": "保存",
"words.search": "搜索",
"words.starred": "收藏",
"words.submit": "提交",
"words.update": "更新",
"words.which.all": "全部"

View File

@ -3,6 +3,35 @@
"cancel": "取消",
"close": "關閉",
"confirm": "確認",
"discover.category.all": "全部",
"discover.category.anime": "ACG",
"discover.category.bbs": "討論區",
"discover.category.blog": "博客",
"discover.category.design": "設計",
"discover.category.finance": "財經",
"discover.category.forecast": "預測",
"discover.category.game": "遊戲",
"discover.category.government": "政府",
"discover.category.journal": "科學期刊",
"discover.category.live": "直播",
"discover.category.multimedia": "多媒體",
"discover.category.new-media": "新媒體",
"discover.category.picture": "圖片",
"discover.category.program-update": "應用程式更新",
"discover.category.programming": "程式設計",
"discover.category.reading": "閱讀",
"discover.category.shopping": "購物",
"discover.category.social-media": "社交媒體",
"discover.category.study": "學習",
"discover.category.traditional-media": "新聞",
"discover.category.travel": "旅遊",
"discover.category.university": "大學",
"feed_view_type.articles": "文章",
"feed_view_type.audios": "音訊",
"feed_view_type.notifications": "通知",
"feed_view_type.pictures": "圖片",
"feed_view_type.social_media": "社交媒體",
"feed_view_type.videos": "影片",
"ok": "確定",
"quantifier.piece": "",
"retry": "重試",
@ -24,10 +53,13 @@
"words.edit": "編輯",
"words.entry": "條目",
"words.expand": "展開",
"words.feeds": "訂閱源",
"words.follow": "追隨",
"words.id": "ID",
"words.inbox": "收件匣",
"words.items_one": "項目",
"words.items_other": "項目",
"words.lists": "清單",
"words.local": "本地",
"words.manage": "管理",
"words.record": "記錄",
@ -39,6 +71,8 @@
"words.result_other": "結果",
"words.rsshub": "RSSHub",
"words.save": "儲存",
"words.search": "搜尋",
"words.starred": "收藏",
"words.submit": "提交",
"words.update": "更新",
"words.which.all": "全部"

View File

@ -3,6 +3,35 @@
"cancel": "取消",
"close": "關閉",
"confirm": "確認",
"discover.category.all": "全部",
"discover.category.anime": "動漫",
"discover.category.bbs": "論壇",
"discover.category.blog": "部落格",
"discover.category.design": "設計",
"discover.category.finance": "金融",
"discover.category.forecast": "預報預警",
"discover.category.game": "遊戲",
"discover.category.government": "政府資訊",
"discover.category.journal": "科學期刊",
"discover.category.live": "直播",
"discover.category.multimedia": "多媒體",
"discover.category.new-media": "新媒體",
"discover.category.picture": "圖片",
"discover.category.program-update": "程式更新",
"discover.category.programming": "程式設計",
"discover.category.reading": "閱讀",
"discover.category.shopping": "購物",
"discover.category.social-media": "社交媒體",
"discover.category.study": "學習",
"discover.category.traditional-media": "傳統媒體",
"discover.category.travel": "出行旅遊",
"discover.category.university": "大學資訊",
"feed_view_type.articles": "文章",
"feed_view_type.audios": "音訊",
"feed_view_type.notifications": "通知",
"feed_view_type.pictures": "圖片",
"feed_view_type.social_media": "社交媒體",
"feed_view_type.videos": "影片",
"ok": "確定",
"quantifier.piece": "條",
"retry": "重試",
@ -23,10 +52,13 @@
"words.edit": "編輯",
"words.entry": "條目",
"words.expand": "展開",
"words.feeds": "RSS 摘要",
"words.follow": "跟隨",
"words.id": "ID",
"words.inbox": "收件匣",
"words.items_one": "內容",
"words.items_other": "內容",
"words.lists": "列表",
"words.local": "本地",
"words.manage": "管理",
"words.record": "記錄",
@ -38,6 +70,8 @@
"words.result_other": "結果",
"words.rsshub": "RSSHub",
"words.save": "儲存",
"words.search": "搜尋",
"words.starred": "收藏",
"words.submit": "提交",
"words.update": "更新",
"words.which.all": "全部"

View File

@ -0,0 +1,8 @@
{
"profile.title": "ملف {{name}} الشخصي",
"profile.uncategorized_feeds": "الخلاصات غير المصنفة",
"tabs.discover": "اكتشف",
"tabs.home": "الرئيسية",
"tabs.settings": "الإعدادات",
"tabs.subscriptions": "الاشتراكات"
}

View File

@ -0,0 +1,8 @@
{
"profile.title": "ملف {{name}} الشخصي",
"profile.uncategorized_feeds": "الخلاصات غير المصنفة",
"tabs.discover": "اكتشف",
"tabs.home": "الرئيسية",
"tabs.settings": "الإعدادات",
"tabs.subscriptions": "الاشتراكات"
}

View File

@ -0,0 +1,8 @@
{
"profile.title": "ملف {{name}} الشخصي",
"profile.uncategorized_feeds": "الخلاصات غير المصنفة",
"tabs.discover": "اكتشف",
"tabs.home": "الرئيسية",
"tabs.settings": "الإعدادات",
"tabs.subscriptions": "الاشتراكات"
}

View File

@ -0,0 +1,8 @@
{
"profile.title": "ملف {{name}} الشخصي",
"profile.uncategorized_feeds": "الخلاصات غير المصنفة",
"tabs.discover": "اكتشف",
"tabs.home": "الرئيسية",
"tabs.settings": "الإعدادات",
"tabs.subscriptions": "الاشتراكات"
}

View File

@ -0,0 +1,8 @@
{
"profile.title": "ملف {{name}} الشخصي",
"profile.uncategorized_feeds": "الخلاصات غير المصنفة",
"tabs.discover": "اكتشف",
"tabs.home": "الرئيسية",
"tabs.settings": "الإعدادات",
"tabs.subscriptions": "الاشتراكات"
}

View File

@ -0,0 +1,8 @@
{
"profile.title": "ملف {{name}} الشخصي",
"profile.uncategorized_feeds": "الخلاصات غير المصنفة",
"tabs.discover": "اكتشف",
"tabs.home": "الرئيسية",
"tabs.settings": "الإعدادات",
"tabs.subscriptions": "الاشتراكات"
}

View File

@ -0,0 +1,8 @@
{
"profile.title": "{{name}}s Profil",
"profile.uncategorized_feeds": "Unkategorisierte Feeds",
"tabs.discover": "Entdecken",
"tabs.home": "Startseite",
"tabs.settings": "Einstellungen",
"tabs.subscriptions": "Abonnements"
}

View File

@ -1,4 +1,6 @@
{
"profile.title": "{{name}}'s Profile",
"profile.uncategorized_feeds": "Uncategorized feeds",
"tabs.discover": "Discover",
"tabs.home": "Home",
"tabs.settings": "Settings",

View File

@ -0,0 +1,8 @@
{
"profile.title": "Perfil de {{name}}",
"profile.uncategorized_feeds": "Feeds sin categorizar",
"tabs.discover": "Descubrir",
"tabs.home": "Inicio",
"tabs.settings": "Configuraciones",
"tabs.subscriptions": "Suscripciones"
}

View File

@ -0,0 +1,8 @@
{
"profile.title": "{{name}}n profiili",
"profile.uncategorized_feeds": "Luokittelemattomat syötteet",
"tabs.discover": "Löydä",
"tabs.home": "Koti",
"tabs.settings": "Asetukset",
"tabs.subscriptions": "Tilaukset"
}

View File

@ -0,0 +1,8 @@
{
"profile.title": "Profil de {{name}}",
"profile.uncategorized_feeds": "Flux non catégorisés",
"tabs.discover": "Découvrir",
"tabs.home": "Accueil",
"tabs.settings": "Paramètres",
"tabs.subscriptions": "Abonnements"
}

View File

@ -0,0 +1,8 @@
{
"profile.title": "Profilo di {{name}}",
"profile.uncategorized_feeds": "Feed non categorizzati",
"tabs.discover": "Scopri",
"tabs.home": "Home",
"tabs.settings": "Impostazioni",
"tabs.subscriptions": "Abbonamenti"
}

View File

@ -0,0 +1,8 @@
{
"profile.title": "{{name}}のプロフィール",
"profile.uncategorized_feeds": "未分類のフィード",
"tabs.discover": "発見",
"tabs.home": "ホーム",
"tabs.settings": "設定",
"tabs.subscriptions": "購読"
}

View File

@ -0,0 +1,8 @@
{
"profile.title": "{{name}}의 프로필",
"profile.uncategorized_feeds": "분류되지 않은 피드",
"tabs.discover": "발견",
"tabs.home": "홈",
"tabs.settings": "설정",
"tabs.subscriptions": "구독"
}

View File

@ -0,0 +1,8 @@
{
"profile.title": "Perfil de {{name}}",
"profile.uncategorized_feeds": "Feeds não categorizados",
"tabs.discover": "Descobrir",
"tabs.home": "Início",
"tabs.settings": "Configurações",
"tabs.subscriptions": "Assinaturas"
}

View File

@ -0,0 +1,8 @@
{
"profile.title": "Профиль {{name}}",
"profile.uncategorized_feeds": "Некатегоризированные ленты",
"tabs.discover": "Открыть",
"tabs.home": "Главная",
"tabs.settings": "Настройки",
"tabs.subscriptions": "Подписки"
}

View File

@ -0,0 +1,8 @@
{
"profile.title": "{{name}}'nin Profili",
"profile.uncategorized_feeds": "Kategorize edilmemiş beslemeler",
"tabs.discover": "Keşfet",
"tabs.home": "Ana Sayfa",
"tabs.settings": "Ayarlar",
"tabs.subscriptions": "Abonelikler"
}

View File

@ -1,4 +1,6 @@
{
"profile.title": "{{name}}的个人资料",
"profile.uncategorized_feeds": "未分类的订阅源",
"tabs.discover": "发现",
"tabs.home": "首页",
"tabs.settings": "设置",

View File

@ -0,0 +1,8 @@
{
"profile.title": "{{name}}的個人資料",
"profile.uncategorized_feeds": "未分類的訂閱源",
"tabs.discover": "探索",
"tabs.home": "主頁",
"tabs.settings": "設定",
"tabs.subscriptions": "訂閱"
}

View File

@ -0,0 +1,8 @@
{
"profile.title": "{{name}}的個人資料",
"profile.uncategorized_feeds": "未分類的訂閱源",
"tabs.discover": "探索",
"tabs.home": "首頁",
"tabs.settings": "設定",
"tabs.subscriptions": "訂閱"
}

View File

@ -359,6 +359,7 @@
"rsshub.useModal.title": "RSSHub Instance",
"rsshub.useModal.useWith": "Use with {{amount}} <Power />",
"titles.about": "About",
"titles.account": "Account",
"titles.actions": "Actions",
"titles.appearance": "Appearance",
"titles.data_control": "Data Control",
@ -367,9 +368,12 @@
"titles.integration": "Integration",
"titles.invitations": "Invitations",
"titles.lists": "Lists",
"titles.notifications": "Notifications",
"titles.power": "Power",
"titles.privacy": "Privacy",
"titles.profile": "Profile",
"titles.shortcuts": "Shortcuts",
"titles.sign_out": "Sign Out",
"wallet.address.title": "Your Address",
"wallet.balance.activePoints": "Active Points",
"wallet.balance.dailyReward": "Your Daily Reward",

View File

@ -350,6 +350,7 @@
"rsshub.useModal.title": "RSSHub 实例",
"rsshub.useModal.useWith": "使用 {{amount}} <Power />",
"titles.about": "关于",
"titles.account": "账户",
"titles.actions": "自动化",
"titles.appearance": "外观",
"titles.data_control": "数据控制",
@ -358,9 +359,12 @@
"titles.integration": "集成",
"titles.invitations": "邀请",
"titles.lists": "列表",
"titles.notifications": "通知",
"titles.power": "Power",
"titles.privacy": "隐私",
"titles.profile": "个人资料",
"titles.shortcuts": "快捷键",
"titles.sign_out": "登出",
"wallet.address.title": "地址",
"wallet.balance.activePoints": "活跃度",
"wallet.balance.dailyReward": "每日奖励",

View File

@ -2,8 +2,14 @@ import * as React from "react"
import { FeedViewType } from "./enums"
interface ViewDefinition {
name: string
export interface ViewDefinition {
name:
| "feed_view_type.articles"
| "feed_view_type.audios"
| "feed_view_type.notifications"
| "feed_view_type.pictures"
| "feed_view_type.social_media"
| "feed_view_type.videos"
icon: React.JSX.Element
className: string
peerClassName: string

View File

@ -0,0 +1,71 @@
import fs from "node:fs"
import path from "node:path"
const sourceDir = "./locales/app"
const targetDir = "./locales/common"
const keysToCopy: string[] = [
"feed_view_type.articles",
"feed_view_type.audios",
"feed_view_type.notifications",
"feed_view_type.pictures",
"feed_view_type.social_media",
"feed_view_type.videos",
// kept the duplicate keys in locales/app
"words.starred",
"words.inbox",
"words.feeds",
"words.lists",
"words.search",
"discover.category.all",
"discover.category.anime",
"discover.category.bbs",
"discover.category.blog",
"discover.category.design",
"discover.category.finance",
"discover.category.forecast",
"discover.category.game",
"discover.category.government",
"discover.category.journal",
"discover.category.live",
"discover.category.multimedia",
"discover.category.new-media",
"discover.category.picture",
"discover.category.program-update",
"discover.category.programming",
"discover.category.reading",
"discover.category.shopping",
"discover.category.social-media",
"discover.category.study",
"discover.category.traditional-media",
"discover.category.travel",
"discover.category.university",
]
const copyTranslations = (sourceDir: string, targetDir: string) => {
const sourceFiles = fs.readdirSync(sourceDir)
sourceFiles.forEach((file) => {
const sourceFilePath = path.join(sourceDir, file)
const targetFilePath = path.join(targetDir, file)
if (fs.statSync(sourceFilePath).isDirectory()) {
// Recursively copy translations from subdirectories
copyTranslations(sourceFilePath, targetFilePath)
} else if (path.extname(file) === ".json") {
const sourceContent = JSON.parse(fs.readFileSync(sourceFilePath, "utf-8"))
const targetContent = fs.existsSync(targetFilePath)
? JSON.parse(fs.readFileSync(targetFilePath, "utf-8"))
: {}
keysToCopy.forEach((key) => {
if (sourceContent[key] && !targetContent[key]) {
targetContent[key] = sourceContent[key]
}
})
fs.writeFileSync(targetFilePath, `${JSON.stringify(targetContent, null, 2)}\n`)
}
})
}
copyTranslations(sourceDir, targetDir)