feat(i18n): merge #4597/#4774 and complete localization audit (#4839)
* chore: Update Japanese translations for new features and errors Added and updated multiple Japanese locale strings across AI, app, errors, external, settings, and shortcuts modules. Changes include new labels, descriptions, error messages, and UI text for features such as scheduling, token usage, inbox management, toolbar customization, and quick search. * Add Japanese README documentation Introduces README-ja.md with comprehensive information about Folo, including features, installation instructions, community links, contribution guidelines, code signing policies, and license details for Japanese-speaking users. * Update formatting in Japanese README Corrected the admonition marker from '[!重要]' to '[!IMPORTANT]' for consistency and made minor formatting adjustments in the README-ja.md file. * Update community section in Japanese README Revised wording and formatting in the community participation section of README-ja.md for improved clarity and consistency. * Remove Japanese README file Deleted README-ja.md to reduce duplication or outdated documentation. All relevant information should now be referenced from the main README or other documentation files. * feat: add French (fr-FR) localization * fix: sort imports in default-resource.ts to resolve lint error * feat: add missing French translations for mobile/default, native, and external locales * feat: add French language filter to discovery/recommended feeds * docs: update changelog for French localization * feat(i18n): merge PR #4597/#4774 and finish localization audit --------- Co-authored-by: dai <dosada@gmail.com> Co-authored-by: Anthony Mahé <snpepito@gmail.com> Co-authored-by: Anthony Mahé <104373430+AnthonyMahe@users.noreply.github.com>
This commit is contained in:
parent
0859f56ad4
commit
01da257c90
|
|
@ -2,10 +2,12 @@
|
||||||
|
|
||||||
## Shiny new things
|
## Shiny new things
|
||||||
|
|
||||||
|
- Supported French localization.
|
||||||
|
|
||||||
## Improvements
|
## Improvements
|
||||||
|
|
||||||
## No longer broken
|
## No longer broken
|
||||||
|
|
||||||
## Thanks
|
## Thanks
|
||||||
|
|
||||||
Special thanks to volunteer contributors @ for their valuable contributions
|
Special thanks to volunteer contributors @AnthonyMahe for their valuable contributions
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
// DONT EDIT THIS FILE MANUALLY
|
// DONT EDIT THIS FILE MANUALLY
|
||||||
const langs = ["en", "zh-CN", "zh-TW", "ja"] as const
|
const langs = ["en", "zh-CN", "zh-TW", "ja", "fr-FR"] as const
|
||||||
export const currentSupportedLanguages = langs as readonly string[]
|
export const currentSupportedLanguages = langs as readonly string[]
|
||||||
export type RendererSupportedLanguages = (typeof langs)[number]
|
export type RendererSupportedLanguages = (typeof langs)[number]
|
||||||
|
|
||||||
|
|
@ -8,6 +8,7 @@ export const dayjsLocaleImportMap = {
|
||||||
["zh-CN"]: ["zh-cn", () => import("dayjs/locale/zh-cn")],
|
["zh-CN"]: ["zh-cn", () => import("dayjs/locale/zh-cn")],
|
||||||
["ja"]: ["ja", () => import("dayjs/locale/ja")],
|
["ja"]: ["ja", () => import("dayjs/locale/ja")],
|
||||||
["zh-TW"]: ["zh-tw", () => import("dayjs/locale/zh-tw")],
|
["zh-TW"]: ["zh-tw", () => import("dayjs/locale/zh-tw")],
|
||||||
|
["fr-FR"]: ["fr", () => import("dayjs/locale/fr")],
|
||||||
}
|
}
|
||||||
export const ns = ["common", "lang", "errors", "app", "settings", "shortcuts", "ai"] as const
|
export const ns = ["common", "lang", "errors", "app", "settings", "shortcuts", "ai"] as const
|
||||||
export const defaultNS = "app" as const
|
export const defaultNS = "app" as const
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,34 @@
|
||||||
// DONT EDIT THIS FILE MANUALLY
|
// DONT EDIT THIS FILE MANUALLY
|
||||||
import ai_en from "@locales/ai/en.json"
|
import ai_en from "@locales/ai/en.json"
|
||||||
|
import ai_frFR from "@locales/ai/fr-FR.json"
|
||||||
import ai_ja from "@locales/ai/ja.json"
|
import ai_ja from "@locales/ai/ja.json"
|
||||||
import en from "@locales/app/en.json"
|
import en from "@locales/app/en.json"
|
||||||
|
import app_frFR from "@locales/app/fr-FR.json"
|
||||||
import app_ja from "@locales/app/ja.json"
|
import app_ja from "@locales/app/ja.json"
|
||||||
import app_zhCN from "@locales/app/zh-CN.json"
|
import app_zhCN from "@locales/app/zh-CN.json"
|
||||||
import app_zhTW from "@locales/app/zh-TW.json"
|
import app_zhTW from "@locales/app/zh-TW.json"
|
||||||
import common_en from "@locales/common/en.json"
|
import common_en from "@locales/common/en.json"
|
||||||
|
import common_frFR from "@locales/common/fr-FR.json"
|
||||||
import common_ja from "@locales/common/ja.json"
|
import common_ja from "@locales/common/ja.json"
|
||||||
import common_zhCN from "@locales/common/zh-CN.json"
|
import common_zhCN from "@locales/common/zh-CN.json"
|
||||||
import common_zhTW from "@locales/common/zh-TW.json"
|
import common_zhTW from "@locales/common/zh-TW.json"
|
||||||
import errors_en from "@locales/errors/en.json"
|
import errors_en from "@locales/errors/en.json"
|
||||||
|
import errors_frFR from "@locales/errors/fr-FR.json"
|
||||||
import errors_ja from "@locales/errors/ja.json"
|
import errors_ja from "@locales/errors/ja.json"
|
||||||
import errors_zhCN from "@locales/errors/zh-CN.json"
|
import errors_zhCN from "@locales/errors/zh-CN.json"
|
||||||
import errors_zhTW from "@locales/errors/zh-TW.json"
|
import errors_zhTW from "@locales/errors/zh-TW.json"
|
||||||
import lang_en from "@locales/lang/en.json"
|
import lang_en from "@locales/lang/en.json"
|
||||||
|
import lang_frFR from "@locales/lang/fr-FR.json"
|
||||||
import lang_ja from "@locales/lang/ja.json"
|
import lang_ja from "@locales/lang/ja.json"
|
||||||
import lang_zhCN from "@locales/lang/zh-CN.json"
|
import lang_zhCN from "@locales/lang/zh-CN.json"
|
||||||
import lang_zhTW from "@locales/lang/zh-TW.json"
|
import lang_zhTW from "@locales/lang/zh-TW.json"
|
||||||
import settings_en from "@locales/settings/en.json"
|
import settings_en from "@locales/settings/en.json"
|
||||||
|
import settings_frFR from "@locales/settings/fr-FR.json"
|
||||||
import settings_ja from "@locales/settings/ja.json"
|
import settings_ja from "@locales/settings/ja.json"
|
||||||
import settings_zhCN from "@locales/settings/zh-CN.json"
|
import settings_zhCN from "@locales/settings/zh-CN.json"
|
||||||
import settings_zhTW from "@locales/settings/zh-TW.json"
|
import settings_zhTW from "@locales/settings/zh-TW.json"
|
||||||
import shortcuts_en from "@locales/shortcuts/en.json"
|
import shortcuts_en from "@locales/shortcuts/en.json"
|
||||||
|
import shortcuts_frFR from "@locales/shortcuts/fr-FR.json"
|
||||||
import shortcuts_ja from "@locales/shortcuts/ja.json"
|
import shortcuts_ja from "@locales/shortcuts/ja.json"
|
||||||
import shortcuts_zhCN from "@locales/shortcuts/zh-CN.json"
|
import shortcuts_zhCN from "@locales/shortcuts/zh-CN.json"
|
||||||
import shortcuts_zhTW from "@locales/shortcuts/zh-TW.json"
|
import shortcuts_zhTW from "@locales/shortcuts/zh-TW.json"
|
||||||
|
|
@ -70,6 +77,15 @@ export const defaultResources = {
|
||||||
errors: errors_zhTW,
|
errors: errors_zhTW,
|
||||||
ai: ai_en, // Fallback to English until Traditional Chinese translation is available
|
ai: ai_en, // Fallback to English until Traditional Chinese translation is available
|
||||||
},
|
},
|
||||||
|
"fr-FR": {
|
||||||
|
app: app_frFR,
|
||||||
|
lang: lang_frFR,
|
||||||
|
common: common_frFR,
|
||||||
|
settings: settings_frFR,
|
||||||
|
shortcuts: shortcuts_frFR,
|
||||||
|
errors: errors_frFR,
|
||||||
|
ai: ai_frFR,
|
||||||
|
},
|
||||||
} satisfies Record<
|
} satisfies Record<
|
||||||
RendererSupportedLanguages,
|
RendererSupportedLanguages,
|
||||||
Partial<Record<(typeof ns)[number], Record<string, string>>>
|
Partial<Record<(typeof ns)[number], Record<string, string>>>
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,13 @@
|
||||||
import ai_en from "@locales/ai/en.json"
|
import ai_en from "@locales/ai/en.json"
|
||||||
import en from "@locales/app/en.json"
|
import en from "@locales/app/en.json"
|
||||||
import common_en from "@locales/common/en.json"
|
import common_en from "@locales/common/en.json"
|
||||||
|
import common_frFR from "@locales/common/fr-FR.json"
|
||||||
import common_ja from "@locales/common/ja.json"
|
import common_ja from "@locales/common/ja.json"
|
||||||
import common_zhCN from "@locales/common/zh-CN.json"
|
import common_zhCN from "@locales/common/zh-CN.json"
|
||||||
import common_zhTW from "@locales/common/zh-TW.json"
|
import common_zhTW from "@locales/common/zh-TW.json"
|
||||||
import errors_en from "@locales/errors/en.json"
|
import errors_en from "@locales/errors/en.json"
|
||||||
import lang_en from "@locales/lang/en.json"
|
import lang_en from "@locales/lang/en.json"
|
||||||
|
import lang_frFR from "@locales/lang/fr-FR.json"
|
||||||
import lang_ja from "@locales/lang/ja.json"
|
import lang_ja from "@locales/lang/ja.json"
|
||||||
import lang_zhCN from "@locales/lang/zh-CN.json"
|
import lang_zhCN from "@locales/lang/zh-CN.json"
|
||||||
import lang_zhTW from "@locales/lang/zh-TW.json"
|
import lang_zhTW from "@locales/lang/zh-TW.json"
|
||||||
|
|
@ -42,6 +44,7 @@ export const defaultResources = {
|
||||||
common: common_ja,
|
common: common_ja,
|
||||||
},
|
},
|
||||||
"zh-TW": { lang: lang_zhTW, common: common_zhTW },
|
"zh-TW": { lang: lang_zhTW, common: common_zhTW },
|
||||||
|
"fr-FR": { lang: lang_frFR, common: common_frFR },
|
||||||
} satisfies Record<
|
} satisfies Record<
|
||||||
RendererSupportedLanguages,
|
RendererSupportedLanguages,
|
||||||
Partial<Record<(typeof ns)[number], Record<string, string>>>
|
Partial<Record<(typeof ns)[number], Record<string, string>>>
|
||||||
|
|
|
||||||
|
|
@ -18,13 +18,17 @@ const LanguageOptions = [
|
||||||
label: "words.english",
|
label: "words.english",
|
||||||
value: "eng",
|
value: "eng",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: "words.french",
|
||||||
|
value: "fra",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: "words.chinese",
|
label: "words.chinese",
|
||||||
value: "cmn",
|
value: "cmn",
|
||||||
},
|
},
|
||||||
] satisfies ResponsiveSelectItem[]
|
] satisfies ResponsiveSelectItem[]
|
||||||
|
|
||||||
type Language = "all" | "eng" | "cmn"
|
type Language = "all" | "eng" | "cmn" | "fra"
|
||||||
type DiscoveryView = "trending" | "categories"
|
type DiscoveryView = "trending" | "categories"
|
||||||
|
|
||||||
export function DiscoveryContent() {
|
export function DiscoveryContent() {
|
||||||
|
|
|
||||||
|
|
@ -25,9 +25,13 @@ const LanguageOptions = [
|
||||||
label: "words.chinese",
|
label: "words.chinese",
|
||||||
value: "cmn",
|
value: "cmn",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: "words.french",
|
||||||
|
value: "fra",
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
type Language = "all" | "eng" | "cmn"
|
type Language = "all" | "eng" | "cmn" | "fra"
|
||||||
|
|
||||||
type View = "all" | string
|
type View = "all" | string
|
||||||
|
|
||||||
|
|
@ -64,6 +68,7 @@ export function Trending({
|
||||||
const { t: tCommon } = useTranslation("common")
|
const { t: tCommon } = useTranslation("common")
|
||||||
const lang = useUISettingKey("discoverLanguage")
|
const lang = useUISettingKey("discoverLanguage")
|
||||||
const { onUpdateMaxScroll } = useScrollElementUpdate()
|
const { onUpdateMaxScroll } = useScrollElementUpdate()
|
||||||
|
const trendingLanguage = lang === "fra" ? "eng" : lang
|
||||||
|
|
||||||
const [selectedView, setSelectedView] = useState<View>("all")
|
const [selectedView, setSelectedView] = useState<View>("all")
|
||||||
const viewOptions = useMemo(() => buildViewOptions(), [])
|
const viewOptions = useMemo(() => buildViewOptions(), [])
|
||||||
|
|
@ -72,7 +77,7 @@ export function Trending({
|
||||||
queryKey: ["trending", lang, selectedView],
|
queryKey: ["trending", lang, selectedView],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
return await followClient.api.trending.getFeeds({
|
return await followClient.api.trending.getFeeds({
|
||||||
language: lang === "all" ? undefined : lang,
|
language: trendingLanguage === "all" ? undefined : trendingLanguage,
|
||||||
view: selectedView === "all" ? undefined : Number(selectedView),
|
view: selectedView === "all" ? undefined : Number(selectedView),
|
||||||
limit,
|
limit,
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ const LanguageMap = {
|
||||||
all: "all",
|
all: "all",
|
||||||
eng: "en",
|
eng: "en",
|
||||||
cmn: "zh-CN",
|
cmn: "zh-CN",
|
||||||
|
fra: "fr-FR",
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export const Component = () => {
|
export const Component = () => {
|
||||||
|
|
@ -134,7 +135,7 @@ export const Component = () => {
|
||||||
) : items.length > 0 ? (
|
) : items.length > 0 ? (
|
||||||
<div className="w-full px-8 pb-8 pt-4">
|
<div className="w-full px-8 pb-8 pt-4">
|
||||||
<Input
|
<Input
|
||||||
placeholder="Search"
|
placeholder={t("words.search", { ns: "common" })}
|
||||||
className="mb-4"
|
className="mb-4"
|
||||||
value={search}
|
value={search}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
|
|
@ -162,12 +163,10 @@ export const Component = () => {
|
||||||
<EmptyIcon />
|
<EmptyIcon />
|
||||||
</div>
|
</div>
|
||||||
<p className="text-title2 text-text">
|
<p className="text-title2 text-text">
|
||||||
{t("common.noContent", { defaultValue: "No content found in this category" })}
|
{t("discover.empty.no_content", { ns: "common" })}
|
||||||
</p>
|
</p>
|
||||||
<p className="mt-2 text-body text-text-secondary">
|
<p className="mt-2 text-body text-text-secondary">
|
||||||
{t("discover.tryAnotherCategory", {
|
{t("discover.empty.try_another_category_or_language", { ns: "common" })}
|
||||||
defaultValue: "Try selecting another category or language",
|
|
||||||
})}
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,12 @@
|
||||||
|
|
||||||
## Shiny new things
|
## Shiny new things
|
||||||
|
|
||||||
|
- Supported French localization.
|
||||||
|
|
||||||
## Improvements
|
## Improvements
|
||||||
|
|
||||||
## No longer broken
|
## No longer broken
|
||||||
|
|
||||||
## Thanks
|
## Thanks
|
||||||
|
|
||||||
Special thanks to volunteer contributors @ for their valuable contributions
|
Special thanks to volunteer contributors @AnthonyMahe for their valuable contributions
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { applicationName } from "expo-application"
|
import { applicationName } from "expo-application"
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
import { TouchableOpacity, View } from "react-native"
|
import { TouchableOpacity, View } from "react-native"
|
||||||
|
|
||||||
import { Text } from "@/src/components/ui/typography/Text"
|
import { Text } from "@/src/components/ui/typography/Text"
|
||||||
|
|
@ -9,22 +10,23 @@ interface GlobalErrorScreenProps {
|
||||||
resetError?: () => void
|
resetError?: () => void
|
||||||
}
|
}
|
||||||
export const GlobalErrorScreen: React.FC<GlobalErrorScreenProps> = ({ error, resetError }) => {
|
export const GlobalErrorScreen: React.FC<GlobalErrorScreenProps> = ({ error, resetError }) => {
|
||||||
|
const { t } = useTranslation("common")
|
||||||
return (
|
return (
|
||||||
<View className="flex-1 bg-system-background">
|
<View className="flex-1 bg-system-background">
|
||||||
<View className="flex-1 items-center justify-center px-6">
|
<View className="flex-1 items-center justify-center px-6">
|
||||||
<Text className="mb-6 text-[64px]">😕</Text>
|
<Text className="mb-6 text-[64px]">😕</Text>
|
||||||
<Text className="mb-3 text-center text-xl font-semibold text-label">
|
<Text className="mb-3 text-center text-xl font-semibold text-label">
|
||||||
{applicationName} crashed!
|
{t("error_screen.crashed", { appName: applicationName ?? "App" })}
|
||||||
</Text>
|
</Text>
|
||||||
<Text className="mb-8 text-center text-lg text-secondary-label">
|
<Text className="mb-8 text-center text-lg text-secondary-label">
|
||||||
{error?.message || "An unexpected error occurred."}
|
{error?.message || t("error_screen.unexpected")}
|
||||||
</Text>
|
</Text>
|
||||||
{resetError && (
|
{resetError && (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
className="min-w-[160px] rounded-xl bg-accent px-6 py-3"
|
className="min-w-[160px] rounded-xl bg-accent px-6 py-3"
|
||||||
onPress={resetError}
|
onPress={resetError}
|
||||||
>
|
>
|
||||||
<Text className="text-center text-[17px] font-semibold text-white">Try Again</Text>
|
<Text className="text-center text-[17px] font-semibold text-white">{t("retry")}</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
import { TouchableOpacity, View } from "react-native"
|
import { TouchableOpacity, View } from "react-native"
|
||||||
import { useColor } from "react-native-uikit-colors"
|
import { useColor } from "react-native-uikit-colors"
|
||||||
|
|
||||||
|
|
@ -7,6 +8,7 @@ import { AlertCuteFiIcon } from "@/src/icons/alert_cute_fi"
|
||||||
import { MonoText } from "../ui/typography/MonoText"
|
import { MonoText } from "../ui/typography/MonoText"
|
||||||
|
|
||||||
export const ListErrorView = ({ error, resetError }: { error: Error; resetError: () => void }) => {
|
export const ListErrorView = ({ error, resetError }: { error: Error; resetError: () => void }) => {
|
||||||
|
const { t } = useTranslation("common")
|
||||||
const red = useColor("red")
|
const red = useColor("red")
|
||||||
return (
|
return (
|
||||||
<View className="flex-1 items-center justify-center p-4">
|
<View className="flex-1 items-center justify-center p-4">
|
||||||
|
|
@ -15,17 +17,17 @@ export const ListErrorView = ({ error, resetError }: { error: Error; resetError:
|
||||||
<AlertCuteFiIcon color={red} height={48} width={48} />
|
<AlertCuteFiIcon color={red} height={48} width={48} />
|
||||||
</View>
|
</View>
|
||||||
<Text className="mb-2 text-center text-lg font-semibold text-label">
|
<Text className="mb-2 text-center text-lg font-semibold text-label">
|
||||||
Unable to Load Content
|
{t("error_screen.list_unable_to_load")}
|
||||||
</Text>
|
</Text>
|
||||||
<Text className="mb-2 text-center text-base text-secondary-label">
|
<Text className="mb-2 text-center text-base text-secondary-label">
|
||||||
There was a problem loading the list. Please try again later.
|
{t("error_screen.list_try_later")}
|
||||||
</Text>
|
</Text>
|
||||||
<MonoText className="mb-4 text-center text-base text-secondary-label">
|
<MonoText className="mb-4 text-center text-base text-secondary-label">
|
||||||
{error.message || "Unknown Error"}
|
{error.message || t("error_screen.unknown")}
|
||||||
</MonoText>
|
</MonoText>
|
||||||
|
|
||||||
<TouchableOpacity className="w-full rounded-xl bg-accent px-6 py-3" onPress={resetError}>
|
<TouchableOpacity className="w-full rounded-xl bg-accent px-6 py-3" onPress={resetError}>
|
||||||
<Text className="text-center text-base font-semibold text-white">Try Again</Text>
|
<Text className="text-center text-base font-semibold text-white">{t("retry")}</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
import { TouchableOpacity, View } from "react-native"
|
import { TouchableOpacity, View } from "react-native"
|
||||||
|
|
||||||
import { Text } from "@/src/components/ui/typography/Text"
|
import { Text } from "@/src/components/ui/typography/Text"
|
||||||
|
|
@ -9,6 +10,7 @@ interface ScreenErrorScreenProps {
|
||||||
resetError?: () => void
|
resetError?: () => void
|
||||||
}
|
}
|
||||||
export const ScreenErrorScreen: React.FC<ScreenErrorScreenProps> = ({ error }) => {
|
export const ScreenErrorScreen: React.FC<ScreenErrorScreenProps> = ({ error }) => {
|
||||||
|
const { t } = useTranslation("common")
|
||||||
const navigation = useNavigation()
|
const navigation = useNavigation()
|
||||||
const canGoBack = useCanBack()
|
const canGoBack = useCanBack()
|
||||||
return (
|
return (
|
||||||
|
|
@ -16,10 +18,10 @@ export const ScreenErrorScreen: React.FC<ScreenErrorScreenProps> = ({ error }) =
|
||||||
<View className="flex-1 items-center justify-center px-6">
|
<View className="flex-1 items-center justify-center px-6">
|
||||||
<Text className="mb-6 text-[64px]">😕</Text>
|
<Text className="mb-6 text-[64px]">😕</Text>
|
||||||
<Text className="mb-3 text-center text-xl font-semibold text-label">
|
<Text className="mb-3 text-center text-xl font-semibold text-label">
|
||||||
This page went wrong, go back and try again.
|
{t("error_screen.page_failed")}
|
||||||
</Text>
|
</Text>
|
||||||
<Text className="mb-8 text-center text-lg text-secondary-label">
|
<Text className="mb-8 text-center text-lg text-secondary-label">
|
||||||
{error?.message || "An unexpected error occurred."}
|
{error?.message || t("error_screen.unexpected")}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<View className="flex-row gap-4">
|
<View className="flex-row gap-4">
|
||||||
|
|
@ -28,7 +30,9 @@ export const ScreenErrorScreen: React.FC<ScreenErrorScreenProps> = ({ error }) =
|
||||||
className="min-w-[160px] rounded-xl bg-accent px-6 py-3"
|
className="min-w-[160px] rounded-xl bg-accent px-6 py-3"
|
||||||
onPress={() => navigation.back()}
|
onPress={() => navigation.back()}
|
||||||
>
|
>
|
||||||
<Text className="text-center text-[17px] font-semibold text-white">Go Back </Text>
|
<Text className="text-center text-[17px] font-semibold text-white">
|
||||||
|
{t("words.back")}
|
||||||
|
</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
|
|
||||||
|
|
@ -95,6 +95,7 @@ export const AISummary: FC<{
|
||||||
const upgradeTitle = t("ai.summary_upgrade_required_title")
|
const upgradeTitle = t("ai.summary_upgrade_required_title")
|
||||||
const upgradeDescription = t("ai.summary_upgrade_required_description")
|
const upgradeDescription = t("ai.summary_upgrade_required_description")
|
||||||
const upgradeCTA = t("ai.summary_upgrade_view_plans")
|
const upgradeCTA = t("ai.summary_upgrade_view_plans")
|
||||||
|
const summaryTitle = t("entry_actions.toggle_ai_summary")
|
||||||
const handleUpgradePress = () => {
|
const handleUpgradePress = () => {
|
||||||
void Haptics.selectionAsync()
|
void Haptics.selectionAsync()
|
||||||
void navigateToPlanScreen()
|
void navigateToPlanScreen()
|
||||||
|
|
@ -163,12 +164,12 @@ export const AISummary: FC<{
|
||||||
<MaskedView
|
<MaskedView
|
||||||
maskElement={
|
maskElement={
|
||||||
<View className="bg-transparent">
|
<View className="bg-transparent">
|
||||||
<Text className="text-[15px] font-semibold">AI Summary</Text>
|
<Text className="text-[15px] font-semibold">{summaryTitle}</Text>
|
||||||
</View>
|
</View>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<LinearGradient colors={["#9333ea", "#2563eb"]} start={[0, 0]} end={[1, 0]}>
|
<LinearGradient colors={["#9333ea", "#2563eb"]} start={[0, 0]} end={[1, 0]}>
|
||||||
<Text className="text-[15px] font-semibold text-transparent">AI Summary</Text>
|
<Text className="text-[15px] font-semibold text-transparent">{summaryTitle}</Text>
|
||||||
</LinearGradient>
|
</LinearGradient>
|
||||||
</MaskedView>
|
</MaskedView>
|
||||||
</View>
|
</View>
|
||||||
|
|
@ -177,7 +178,7 @@ export const AISummary: FC<{
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
Clipboard.setString(summaryTextForSheet)
|
Clipboard.setString(summaryTextForSheet)
|
||||||
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success)
|
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success)
|
||||||
toast.success("Copied to clipboard")
|
toast.success(t("app.copied_to_clipboard", { ns: "common" }))
|
||||||
}}
|
}}
|
||||||
onLongPress={() => setSheetOpen(true)}
|
onLongPress={() => setSheetOpen(true)}
|
||||||
className="rounded-full bg-quaternary-system-fill p-1.5 active:opacity-70"
|
className="rounded-full bg-quaternary-system-fill p-1.5 active:opacity-70"
|
||||||
|
|
@ -230,6 +231,7 @@ const ErrorContent = ({
|
||||||
message: string
|
message: string
|
||||||
onRetry?: () => void
|
onRetry?: () => void
|
||||||
}) => {
|
}) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
return (
|
return (
|
||||||
<View className="mt-3">
|
<View className="mt-3">
|
||||||
<View className="flex-row items-center gap-2">
|
<View className="flex-row items-center gap-2">
|
||||||
|
|
@ -238,14 +240,18 @@ const ErrorContent = ({
|
||||||
{onRetry &&
|
{onRetry &&
|
||||||
(forMeasurement ? (
|
(forMeasurement ? (
|
||||||
<View className="mt-3 self-start rounded-full bg-quaternary-system-fill px-4 py-2">
|
<View className="mt-3 self-start rounded-full bg-quaternary-system-fill px-4 py-2">
|
||||||
<Text className="text-[14px] font-medium text-label">Retry</Text>
|
<Text className="text-[14px] font-medium text-label">
|
||||||
|
{t("retry", { ns: "common" })}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
) : (
|
) : (
|
||||||
<Pressable
|
<Pressable
|
||||||
onPress={onRetry}
|
onPress={onRetry}
|
||||||
className="mt-3 self-start rounded-full bg-quaternary-system-fill px-4 py-2"
|
className="mt-3 self-start rounded-full bg-quaternary-system-fill px-4 py-2"
|
||||||
>
|
>
|
||||||
<Text className="text-[14px] font-medium text-label">Retry</Text>
|
<Text className="text-[14px] font-medium text-label">
|
||||||
|
{t("retry", { ns: "common" })}
|
||||||
|
</Text>
|
||||||
</Pressable>
|
</Pressable>
|
||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
|
|
@ -312,6 +318,7 @@ const SelectableTextSheet: FC<{
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
text: string
|
text: string
|
||||||
}> = ({ visible, onClose, text }) => {
|
}> = ({ visible, onClose, text }) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
const insets = useSafeAreaInsets()
|
const insets = useSafeAreaInsets()
|
||||||
const textColor = useColor("label")
|
const textColor = useColor("label")
|
||||||
const handleCopyAll = () => {
|
const handleCopyAll = () => {
|
||||||
|
|
@ -334,7 +341,9 @@ const SelectableTextSheet: FC<{
|
||||||
>
|
>
|
||||||
<CopyCuteReIcon width={18} height={18} color={textColor} />
|
<CopyCuteReIcon width={18} height={18} color={textColor} />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<Text className="text-lg font-semibold text-label">AI Summary</Text>
|
<Text className="text-lg font-semibold text-label">
|
||||||
|
{t("entry_actions.toggle_ai_summary")}
|
||||||
|
</Text>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={onClose}
|
onPress={onClose}
|
||||||
className="rounded-full bg-zinc-100 p-2 active:opacity-80 dark:bg-zinc-800"
|
className="rounded-full bg-zinc-100 p-2 active:opacity-80 dark:bg-zinc-800"
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import { DiscoverSettingsScreen } from "@/src/screens/(modal)/DiscoverSettingsSc
|
||||||
import { RecommendationCategoryScreen } from "@/src/screens/(stack)/recommendation/RecommendationCategoryScreen"
|
import { RecommendationCategoryScreen } from "@/src/screens/(stack)/recommendation/RecommendationCategoryScreen"
|
||||||
|
|
||||||
export const Category = () => {
|
export const Category = () => {
|
||||||
|
const { t } = useTranslation("common")
|
||||||
const navigation = useNavigation()
|
const navigation = useNavigation()
|
||||||
const label = useColor("label")
|
const label = useColor("label")
|
||||||
return (
|
return (
|
||||||
|
|
@ -25,7 +26,9 @@ export const Category = () => {
|
||||||
<View className="mt-4 flex-row items-center justify-between pb-1 pl-6 pr-5 pt-4">
|
<View className="mt-4 flex-row items-center justify-between pb-1 pl-6 pr-5 pt-4">
|
||||||
<View className="flex-row items-center gap-2">
|
<View className="flex-row items-center gap-2">
|
||||||
<Grid2CuteReIcon width={24} height={24} color={label} />
|
<Grid2CuteReIcon width={24} height={24} color={label} />
|
||||||
<Text className="pb-2 text-2xl font-bold leading-[1.1] text-label">Categories</Text>
|
<Text className="pb-2 text-2xl font-bold leading-[1.1] text-label">
|
||||||
|
{t("words.categories")}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<ItemPressable
|
<ItemPressable
|
||||||
className="rounded-lg p-1"
|
className="rounded-lg p-1"
|
||||||
|
|
|
||||||
|
|
@ -131,11 +131,6 @@ export const Recommendations = () => {
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
const LanguageMap = {
|
|
||||||
all: "all",
|
|
||||||
eng: "en",
|
|
||||||
cmn: "zh-CN",
|
|
||||||
} as const
|
|
||||||
export const RecommendationTab: TabComponent<{
|
export const RecommendationTab: TabComponent<{
|
||||||
contentContainerStyle?: StyleProp<ViewStyle>
|
contentContainerStyle?: StyleProp<ViewStyle>
|
||||||
insets?: {
|
insets?: {
|
||||||
|
|
@ -151,11 +146,11 @@ export const RecommendationTab: TabComponent<{
|
||||||
insets: customInsets,
|
insets: customInsets,
|
||||||
...rest
|
...rest
|
||||||
}) => {
|
}) => {
|
||||||
|
const { t } = useTranslation("common")
|
||||||
const discoverLanguage = useUISettingKey("discoverLanguage")
|
const discoverLanguage = useUISettingKey("discoverLanguage")
|
||||||
const { data, isLoading } = useQuery({
|
const { data, isLoading } = useQuery({
|
||||||
queryKey: ["rsshub-popular", tab.value, discoverLanguage],
|
queryKey: ["rsshub-popular", tab.value, discoverLanguage],
|
||||||
queryFn: () =>
|
queryFn: () => fetchRsshubPopular(tab.value, discoverLanguage).then((res) => res.data),
|
||||||
fetchRsshubPopular(tab.value, LanguageMap[discoverLanguage]).then((res) => res.data),
|
|
||||||
staleTime: 1000 * 60 * 60 * 24,
|
staleTime: 1000 * 60 * 60 * 24,
|
||||||
// 1 day
|
// 1 day
|
||||||
meta: {
|
meta: {
|
||||||
|
|
@ -164,7 +159,7 @@ export const RecommendationTab: TabComponent<{
|
||||||
})
|
})
|
||||||
const { data: analysisData, isLoading: isAnalysisLoading } = useQuery({
|
const { data: analysisData, isLoading: isAnalysisLoading } = useQuery({
|
||||||
queryKey: ["rsshub-analysis", discoverLanguage],
|
queryKey: ["rsshub-analysis", discoverLanguage],
|
||||||
queryFn: () => fetchRsshubAnalysis(LanguageMap[discoverLanguage]).then((res) => res.data),
|
queryFn: () => fetchRsshubAnalysis(discoverLanguage).then((res) => res.data),
|
||||||
staleTime: 1000 * 60 * 60 * 24,
|
staleTime: 1000 * 60 * 60 * 24,
|
||||||
// 1 day
|
// 1 day
|
||||||
meta: {
|
meta: {
|
||||||
|
|
@ -259,7 +254,7 @@ export const RecommendationTab: TabComponent<{
|
||||||
if (keys.length === 0) {
|
if (keys.length === 0) {
|
||||||
return (
|
return (
|
||||||
<View className="flex-1 items-center justify-center">
|
<View className="flex-1 items-center justify-center">
|
||||||
<Text className="text-secondary-label">This is a barren wasteland of knowledge.</Text>
|
<Text className="text-secondary-label">{t("search.empty.no_results")}</Text>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
import { useAtom } from "jotai"
|
import { useAtom } from "jotai"
|
||||||
import type { FC } from "react"
|
import type { FC } from "react"
|
||||||
|
import { useMemo } from "react"
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
import type { Animated } from "react-native"
|
import type { Animated } from "react-native"
|
||||||
|
|
||||||
import { TabBar } from "@/src/components/ui/tabview/TabBar"
|
import { TabBar } from "@/src/components/ui/tabview/TabBar"
|
||||||
|
|
@ -11,17 +13,26 @@ import { useSearchPageContext } from "./ctx"
|
||||||
export const SearchTabBar: FC<{
|
export const SearchTabBar: FC<{
|
||||||
animatedX: Animated.Value
|
animatedX: Animated.Value
|
||||||
}> = ({ animatedX }) => {
|
}> = ({ animatedX }) => {
|
||||||
|
const { t } = useTranslation("common")
|
||||||
const { searchTypeAtom } = useSearchPageContext()
|
const { searchTypeAtom } = useSearchPageContext()
|
||||||
const [searchType, setSearchType] = useAtom(searchTypeAtom)
|
const [searchType, setSearchType] = useAtom(searchTypeAtom)
|
||||||
|
const tabs = useMemo(
|
||||||
|
() =>
|
||||||
|
SearchTabs.map((tab) => ({
|
||||||
|
...tab,
|
||||||
|
name: t(tab.name),
|
||||||
|
})),
|
||||||
|
[t],
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TabBar
|
<TabBar
|
||||||
tabbarClassName="border-b border-b-opaque-separator/40"
|
tabbarClassName="border-b border-b-opaque-separator/40"
|
||||||
tabScrollContainerAnimatedX={animatedX}
|
tabScrollContainerAnimatedX={animatedX}
|
||||||
tabs={SearchTabs}
|
tabs={tabs}
|
||||||
currentTab={SearchTabs.findIndex((tab) => tab.value === searchType)}
|
currentTab={tabs.findIndex((tab) => tab.value === searchType)}
|
||||||
onTabItemPress={(index) => {
|
onTabItemPress={(index) => {
|
||||||
setSearchType(SearchTabs[index]!.value as SearchType)
|
setSearchType(tabs[index]!.value as SearchType)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { cn, formatNumber } from "@follow/utils"
|
import { cn, formatNumber } from "@follow/utils"
|
||||||
import { useQuery } from "@tanstack/react-query"
|
import { useQuery } from "@tanstack/react-query"
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
import { View } from "react-native"
|
import { View } from "react-native"
|
||||||
import { useColor } from "react-native-uikit-colors"
|
import { useColor } from "react-native-uikit-colors"
|
||||||
|
|
||||||
|
|
@ -24,13 +25,15 @@ export const Trending = ({
|
||||||
className?: string
|
className?: string
|
||||||
itemClassName?: string
|
itemClassName?: string
|
||||||
}) => {
|
}) => {
|
||||||
|
const { t } = useTranslation("common")
|
||||||
const label = useColor("label")
|
const label = useColor("label")
|
||||||
const discoverLanguage = useUISettingKey("discoverLanguage")
|
const discoverLanguage = useUISettingKey("discoverLanguage")
|
||||||
|
const trendingLanguage = discoverLanguage === "fra" ? "eng" : discoverLanguage
|
||||||
const { data, isLoading } = useQuery({
|
const { data, isLoading } = useQuery({
|
||||||
queryKey: ["trending", "feeds", discoverLanguage],
|
queryKey: ["trending", "feeds", discoverLanguage],
|
||||||
queryFn: () =>
|
queryFn: () =>
|
||||||
fetchFeedTrending({
|
fetchFeedTrending({
|
||||||
lang: discoverLanguage === "all" ? undefined : discoverLanguage,
|
lang: trendingLanguage === "all" ? undefined : trendingLanguage,
|
||||||
limit: 20,
|
limit: 20,
|
||||||
}).then((res) => res.data),
|
}).then((res) => res.data),
|
||||||
meta: {
|
meta: {
|
||||||
|
|
@ -43,7 +46,9 @@ export const Trending = ({
|
||||||
<View className={cn("flex-row items-center justify-between pb-1 pt-4", itemClassName)}>
|
<View className={cn("flex-row items-center justify-between pb-1 pt-4", itemClassName)}>
|
||||||
<View className="flex-row items-center gap-2">
|
<View className="flex-row items-center gap-2">
|
||||||
<TrendingUpCuteReIcon width={24} height={24} color={label} />
|
<TrendingUpCuteReIcon width={24} height={24} color={label} />
|
||||||
<Text className="pb-2 text-2xl font-bold leading-[1.1] text-label">Trending</Text>
|
<Text className="pb-2 text-2xl font-bold leading-[1.1] text-label">
|
||||||
|
{t("words.trending")}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<ItemPressable
|
<ItemPressable
|
||||||
className="rounded-lg p-1"
|
className="rounded-lg p-1"
|
||||||
|
|
|
||||||
|
|
@ -7,25 +7,10 @@ export enum SearchType {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SearchTabs = [
|
export const SearchTabs = [
|
||||||
{ name: "Feed", value: SearchType.Feed },
|
{ name: "words.feeds", value: SearchType.Feed },
|
||||||
{ name: "List", value: SearchType.List },
|
{ name: "words.lists", value: SearchType.List },
|
||||||
// { name: "User", value: SearchType.User },
|
// { name: "User", value: SearchType.User },
|
||||||
]
|
|
||||||
|
|
||||||
const _languageOptions = [
|
|
||||||
{
|
|
||||||
label: "All",
|
|
||||||
value: "all",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "English",
|
|
||||||
value: "en",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "中文",
|
|
||||||
value: "zh-CN",
|
|
||||||
},
|
|
||||||
] as const
|
] as const
|
||||||
|
|
||||||
export type Language = (typeof _languageOptions)[number]["value"]
|
export type Language = "all" | "eng" | "cmn" | "fra"
|
||||||
export type DiscoverCategories = (typeof RSSHubCategories)[number] | string
|
export type DiscoverCategories = (typeof RSSHubCategories)[number] | string
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { useQuery } from "@tanstack/react-query"
|
import { useQuery } from "@tanstack/react-query"
|
||||||
import { useAtomValue } from "jotai"
|
import { useAtomValue } from "jotai"
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
import { useWindowDimensions, View } from "react-native"
|
import { useWindowDimensions, View } from "react-native"
|
||||||
|
|
||||||
import { Text } from "@/src/components/ui/typography/Text"
|
import { Text } from "@/src/components/ui/typography/Text"
|
||||||
|
|
@ -11,6 +12,7 @@ import { useDataSkeleton } from "./hooks"
|
||||||
import { SearchFeedCard } from "./SearchFeedCard"
|
import { SearchFeedCard } from "./SearchFeedCard"
|
||||||
|
|
||||||
export const SearchFeed = () => {
|
export const SearchFeed = () => {
|
||||||
|
const { t } = useTranslation("common")
|
||||||
const { searchValueAtom } = useSearchPageContext()
|
const { searchValueAtom } = useSearchPageContext()
|
||||||
const searchValue = useAtomValue(searchValueAtom)
|
const searchValue = useAtomValue(searchValueAtom)
|
||||||
const windowWidth = useWindowDimensions().width
|
const windowWidth = useWindowDimensions().width
|
||||||
|
|
@ -24,13 +26,18 @@ export const SearchFeed = () => {
|
||||||
const skeleton = useDataSkeleton(isLoading, data)
|
const skeleton = useDataSkeleton(isLoading, data)
|
||||||
if (skeleton) return skeleton
|
if (skeleton) return skeleton
|
||||||
if (data === undefined) return null
|
if (data === undefined) return null
|
||||||
|
const resultCount = data.data?.length ?? 0
|
||||||
|
const resultLabel =
|
||||||
|
resultCount === 0
|
||||||
|
? t("discover.search.results_zero")
|
||||||
|
: t("discover.search.results_other", { count: resultCount })
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
width: windowWidth,
|
width: windowWidth,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Text className="px-6 pt-4 text-text/60">Found {data.data?.length} feeds</Text>
|
<Text className="px-6 pt-4 text-text/60">{resultLabel}</Text>
|
||||||
<View>
|
<View>
|
||||||
{data.data?.map((item) => (
|
{data.data?.map((item) => (
|
||||||
<View key={item.feed?.id || Math.random().toString()}>
|
<View key={item.feed?.id || Math.random().toString()}>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { useSubscriptionByFeedId } from "@follow/store/subscription/hooks"
|
import { useSubscriptionByFeedId } from "@follow/store/subscription/hooks"
|
||||||
import { formatNumber } from "@follow/utils"
|
import { formatNumber } from "@follow/utils"
|
||||||
import type { DiscoveryItem, TrendingFeedItem } from "@follow-app/client-sdk"
|
import type { DiscoveryItem, TrendingFeedItem } from "@follow-app/client-sdk"
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
import { View } from "react-native"
|
import { View } from "react-native"
|
||||||
|
|
||||||
import { RelativeDateTime } from "@/src/components/ui/datetime/RelativeDateTime"
|
import { RelativeDateTime } from "@/src/components/ui/datetime/RelativeDateTime"
|
||||||
|
|
@ -13,15 +14,17 @@ import { useColor } from "@/src/theme/colors"
|
||||||
import { FeedSummary } from "../FeedSummary"
|
import { FeedSummary } from "../FeedSummary"
|
||||||
|
|
||||||
export const SearchFeedCard = ({ item }: { item: TrendingFeedItem | DiscoveryItem }) => {
|
export const SearchFeedCard = ({ item }: { item: TrendingFeedItem | DiscoveryItem }) => {
|
||||||
|
const { t } = useTranslation("common")
|
||||||
const isSubscribed = useSubscriptionByFeedId(item.feed?.id ?? "")
|
const isSubscribed = useSubscriptionByFeedId(item.feed?.id ?? "")
|
||||||
const iconColor = useColor("secondaryLabel")
|
const iconColor = useColor("secondaryLabel")
|
||||||
|
const followerCount = item.analytics?.subscriptionCount || 0
|
||||||
return (
|
return (
|
||||||
<FeedSummary feed={item.feed!} className="py-4 pl-4">
|
<FeedSummary feed={item.feed!} className="py-4 pl-4">
|
||||||
<View className="mt-4 flex-row items-center gap-6">
|
<View className="mt-4 flex-row items-center gap-6">
|
||||||
<View className="flex-row items-center gap-1.5">
|
<View className="flex-row items-center gap-1.5">
|
||||||
<User3CuteReIcon width={14} height={14} color={iconColor} />
|
<User3CuteReIcon width={14} height={14} color={iconColor} />
|
||||||
<Text className="text-sm text-secondary-label">
|
<Text className="text-sm text-secondary-label">
|
||||||
{formatNumber(item.analytics?.subscriptionCount || 0)} followers
|
{formatNumber(followerCount)} {t("feed.follower_other")}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View className="flex-row items-center gap-1.5">
|
<View className="flex-row items-center gap-1.5">
|
||||||
|
|
@ -29,13 +32,13 @@ export const SearchFeedCard = ({ item }: { item: TrendingFeedItem | DiscoveryIte
|
||||||
<>
|
<>
|
||||||
<SafetyCertificateCuteReIcon width={14} height={14} color={iconColor} />
|
<SafetyCertificateCuteReIcon width={14} height={14} color={iconColor} />
|
||||||
<Text className="text-sm text-secondary-label">
|
<Text className="text-sm text-secondary-label">
|
||||||
{item.analytics.updatesPerWeek} entries/week
|
{t("feed.entry_week_other", { count: item.analytics.updatesPerWeek })}
|
||||||
</Text>
|
</Text>
|
||||||
</>
|
</>
|
||||||
) : item.analytics?.latestEntryPublishedAt ? (
|
) : item.analytics?.latestEntryPublishedAt ? (
|
||||||
<>
|
<>
|
||||||
<SafeAlertCuteReIcon width={14} height={14} color={iconColor} />
|
<SafeAlertCuteReIcon width={14} height={14} color={iconColor} />
|
||||||
<Text className="text-sm text-secondary-label">Updated</Text>
|
<Text className="text-sm text-secondary-label">{t("feed.updated_at")}</Text>
|
||||||
<RelativeDateTime
|
<RelativeDateTime
|
||||||
className="text-sm text-secondary-label"
|
className="text-sm text-secondary-label"
|
||||||
date={new Date(item.analytics.latestEntryPublishedAt)}
|
date={new Date(item.analytics.latestEntryPublishedAt)}
|
||||||
|
|
@ -46,11 +49,13 @@ export const SearchFeedCard = ({ item }: { item: TrendingFeedItem | DiscoveryIte
|
||||||
<View className="ml-auto mr-4 mt-1">
|
<View className="ml-auto mr-4 mt-1">
|
||||||
{isSubscribed ? (
|
{isSubscribed ? (
|
||||||
<View className="px-5 py-2">
|
<View className="px-5 py-2">
|
||||||
<Text className="text-sm font-bold text-tertiary-label">Followed</Text>
|
<Text className="text-sm font-bold text-tertiary-label">
|
||||||
|
{t("feed.actions.followed")}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
) : (
|
) : (
|
||||||
<View className="rounded-full bg-accent px-5 py-2">
|
<View className="rounded-full bg-accent px-5 py-2">
|
||||||
<Text className="text-sm font-bold text-white">Follow</Text>
|
<Text className="text-sm font-bold text-white">{t("feed.actions.follow")}</Text>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { formatNumber } from "@follow/utils"
|
||||||
import { useQuery } from "@tanstack/react-query"
|
import { useQuery } from "@tanstack/react-query"
|
||||||
import { useAtomValue } from "jotai"
|
import { useAtomValue } from "jotai"
|
||||||
import { memo } from "react"
|
import { memo } from "react"
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
import { useWindowDimensions, View } from "react-native"
|
import { useWindowDimensions, View } from "react-native"
|
||||||
|
|
||||||
import { FallbackIcon } from "@/src/components/ui/icon/fallback-icon"
|
import { FallbackIcon } from "@/src/components/ui/icon/fallback-icon"
|
||||||
|
|
@ -26,6 +27,7 @@ type SearchResultItem = Awaited<
|
||||||
ReturnType<typeof followClient.api.discover.discover>
|
ReturnType<typeof followClient.api.discover.discover>
|
||||||
>["data"][number]
|
>["data"][number]
|
||||||
export const SearchList = () => {
|
export const SearchList = () => {
|
||||||
|
const { t } = useTranslation("common")
|
||||||
const { searchValueAtom } = useSearchPageContext()
|
const { searchValueAtom } = useSearchPageContext()
|
||||||
const searchValue = useAtomValue(searchValueAtom)
|
const searchValue = useAtomValue(searchValueAtom)
|
||||||
const windowWidth = useWindowDimensions().width
|
const windowWidth = useWindowDimensions().width
|
||||||
|
|
@ -37,13 +39,18 @@ export const SearchList = () => {
|
||||||
const skeleton = useDataSkeleton(isLoading, data)
|
const skeleton = useDataSkeleton(isLoading, data)
|
||||||
if (skeleton) return skeleton
|
if (skeleton) return skeleton
|
||||||
if (data === undefined) return null
|
if (data === undefined) return null
|
||||||
|
const resultCount = data.data?.length ?? 0
|
||||||
|
const resultLabel =
|
||||||
|
resultCount === 0
|
||||||
|
? t("discover.search.results_zero")
|
||||||
|
: t("discover.search.results_other", { count: resultCount })
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
width: windowWidth,
|
width: windowWidth,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Text className="px-6 pt-4 text-text/60">Found {data.data?.length} lists</Text>
|
<Text className="px-6 pt-4 text-text/60">{resultLabel}</Text>
|
||||||
<View>
|
<View>
|
||||||
{data.data?.map((item) => (
|
{data.data?.map((item) => (
|
||||||
<View key={item.feed?.id || Math.random().toString()}>
|
<View key={item.feed?.id || Math.random().toString()}>
|
||||||
|
|
@ -56,9 +63,11 @@ export const SearchList = () => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
const SearchListCard = memo(({ item }: { item: SearchResultItem }) => {
|
const SearchListCard = memo(({ item }: { item: SearchResultItem }) => {
|
||||||
|
const { t } = useTranslation("common")
|
||||||
const isSubscribed = useSubscriptionByListId(item.list?.id ?? "")
|
const isSubscribed = useSubscriptionByListId(item.list?.id ?? "")
|
||||||
const navigation = useNavigation()
|
const navigation = useNavigation()
|
||||||
const iconColor = useColor("text")
|
const iconColor = useColor("text")
|
||||||
|
const followerCount = item.analytics?.subscriptionCount || 0
|
||||||
return (
|
return (
|
||||||
<ItemPressable
|
<ItemPressable
|
||||||
itemStyle={ItemPressableStyle.Plain}
|
itemStyle={ItemPressableStyle.Plain}
|
||||||
|
|
@ -105,13 +114,13 @@ const SearchListCard = memo(({ item }: { item: SearchResultItem }) => {
|
||||||
{isSubscribed ? (
|
{isSubscribed ? (
|
||||||
<View className="ml-auto">
|
<View className="ml-auto">
|
||||||
<View className="rounded-lg bg-gray-5/60 px-3 py-2">
|
<View className="rounded-lg bg-gray-5/60 px-3 py-2">
|
||||||
<Text className="text-sm font-bold text-gray-2">Followed</Text>
|
<Text className="text-sm font-bold text-gray-2">{t("feed.actions.followed")}</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
) : (
|
) : (
|
||||||
<View className="ml-auto">
|
<View className="ml-auto">
|
||||||
<View className="rounded-lg bg-accent px-3 py-2">
|
<View className="rounded-lg bg-accent px-3 py-2">
|
||||||
<Text className="text-sm font-bold text-white">Follow</Text>
|
<Text className="text-sm font-bold text-white">{t("feed.actions.follow")}</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
@ -126,7 +135,7 @@ const SearchListCard = memo(({ item }: { item: SearchResultItem }) => {
|
||||||
<View className="flex-row items-center gap-2">
|
<View className="flex-row items-center gap-2">
|
||||||
<User3CuteReIcon width={16} height={16} color={iconColor} />
|
<User3CuteReIcon width={16} height={16} color={iconColor} />
|
||||||
<Text className="text-text">
|
<Text className="text-text">
|
||||||
{formatNumber(item.analytics?.subscriptionCount || 0)} followers
|
{formatNumber(followerCount)} {t("feed.follower_other")}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { withOpacity } from "@follow/utils"
|
import { withOpacity } from "@follow/utils"
|
||||||
import { useMemo } from "react"
|
import { useMemo } from "react"
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
|
|
||||||
import { PlatformActivityIndicator } from "@/src/components/ui/loading/PlatformActivityIndicator"
|
import { PlatformActivityIndicator } from "@/src/components/ui/loading/PlatformActivityIndicator"
|
||||||
import { Text } from "@/src/components/ui/typography/Text"
|
import { Text } from "@/src/components/ui/typography/Text"
|
||||||
|
|
@ -9,6 +10,7 @@ import { useColor } from "@/src/theme/colors"
|
||||||
import { BaseSearchPageRootView } from "./__base"
|
import { BaseSearchPageRootView } from "./__base"
|
||||||
|
|
||||||
export const useDataSkeleton = (isLoading: boolean, data: any) => {
|
export const useDataSkeleton = (isLoading: boolean, data: any) => {
|
||||||
|
const { t } = useTranslation("common")
|
||||||
const textColor = useColor("text")
|
const textColor = useColor("text")
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
|
|
@ -22,10 +24,10 @@ export const useDataSkeleton = (isLoading: boolean, data: any) => {
|
||||||
return (
|
return (
|
||||||
<BaseSearchPageRootView className="h-64 items-center justify-center">
|
<BaseSearchPageRootView className="h-64 items-center justify-center">
|
||||||
<SadCuteReIcon height={32} width={32} color={withOpacity(textColor, 0.5)} />
|
<SadCuteReIcon height={32} width={32} color={withOpacity(textColor, 0.5)} />
|
||||||
<Text className="mt-2 text-text/50">No results found</Text>
|
<Text className="mt-2 text-text/50">{t("search.empty.no_results")}</Text>
|
||||||
</BaseSearchPageRootView>
|
</BaseSearchPageRootView>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}, [isLoading, data, textColor])
|
}, [isLoading, data, t, textColor])
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
import { View } from "react-native"
|
import { View } from "react-native"
|
||||||
|
|
||||||
import { useGeneralSettingKey } from "@/src/atoms/settings/general"
|
import { useGeneralSettingKey } from "@/src/atoms/settings/general"
|
||||||
|
|
@ -6,6 +7,7 @@ import { CelebrateCuteReIcon } from "@/src/icons/celebrate_cute_re"
|
||||||
import { useColor } from "@/src/theme/colors"
|
import { useColor } from "@/src/theme/colors"
|
||||||
|
|
||||||
export const EntryListEmpty = () => {
|
export const EntryListEmpty = () => {
|
||||||
|
const { t } = useTranslation()
|
||||||
const unreadOnly = useGeneralSettingKey("unreadOnly")
|
const unreadOnly = useGeneralSettingKey("unreadOnly")
|
||||||
const color = useColor("secondaryLabel")
|
const color = useColor("secondaryLabel")
|
||||||
return (
|
return (
|
||||||
|
|
@ -13,10 +15,14 @@ export const EntryListEmpty = () => {
|
||||||
{unreadOnly ? (
|
{unreadOnly ? (
|
||||||
<>
|
<>
|
||||||
<CelebrateCuteReIcon height={30} width={30} color={color} />
|
<CelebrateCuteReIcon height={30} width={30} color={color} />
|
||||||
<Text className="text-lg font-medium text-secondary-label">Zero Unread</Text>
|
<Text className="text-lg font-medium text-secondary-label">
|
||||||
|
{t("entry_list.zero_unread")}
|
||||||
|
</Text>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<Text className="text-secondary-label">No entries</Text>
|
<Text className="text-secondary-label">
|
||||||
|
{t("search.empty.no_results", { ns: "common" })}
|
||||||
|
</Text>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import { uniqBy } from "es-toolkit/compat"
|
||||||
import type { ImageSource } from "expo-image"
|
import type { ImageSource } from "expo-image"
|
||||||
import type { Ref } from "react"
|
import type { Ref } from "react"
|
||||||
import { useMemo } from "react"
|
import { useMemo } from "react"
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
import { View } from "react-native"
|
import { View } from "react-native"
|
||||||
import { measure, runOnJS, runOnUI, useAnimatedRef } from "react-native-reanimated"
|
import { measure, runOnJS, runOnUI, useAnimatedRef } from "react-native-reanimated"
|
||||||
|
|
||||||
|
|
@ -16,6 +17,7 @@ import { useLightboxControls } from "@/src/components/ui/lightbox/lightboxState"
|
||||||
import { Text } from "@/src/components/ui/typography/Text"
|
import { Text } from "@/src/components/ui/typography/Text"
|
||||||
|
|
||||||
export function EntryPictureItem({ id }: { id: string }) {
|
export function EntryPictureItem({ id }: { id: string }) {
|
||||||
|
const { t } = useTranslation()
|
||||||
const { openLightbox } = useLightboxControls()
|
const { openLightbox } = useLightboxControls()
|
||||||
const aviRef = useAnimatedRef<View>()
|
const aviRef = useAnimatedRef<View>()
|
||||||
const item = useEntry(id, (state) => ({
|
const item = useEntry(id, (state) => ({
|
||||||
|
|
@ -36,7 +38,7 @@ export function EntryPictureItem({ id }: { id: string }) {
|
||||||
aspectRatio: 16 / 9,
|
aspectRatio: 16 / 9,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Text className="text-center text-label">No media available</Text>
|
<Text className="text-center text-label">{t("entry_content.no_content")}</Text>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { unreadSyncService } from "@follow/store/unread/store"
|
||||||
import { tracker } from "@follow/tracker"
|
import { tracker } from "@follow/tracker"
|
||||||
import { formatDuration, transformVideoUrl } from "@follow/utils"
|
import { formatDuration, transformVideoUrl } from "@follow/utils"
|
||||||
import { memo, useMemo } from "react"
|
import { memo, useMemo } from "react"
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
import { Linking, View } from "react-native"
|
import { Linking, View } from "react-native"
|
||||||
|
|
||||||
import { getGeneralSettings } from "@/src/atoms/settings/general"
|
import { getGeneralSettings } from "@/src/atoms/settings/general"
|
||||||
|
|
@ -18,6 +19,7 @@ import { VideoContextMenu } from "../../context-menu/video"
|
||||||
import { EntryGridFooter } from "../../entry-content/EntryGridFooter"
|
import { EntryGridFooter } from "../../entry-content/EntryGridFooter"
|
||||||
|
|
||||||
export const EntryVideoItem = memo(({ id }: { id: string }) => {
|
export const EntryVideoItem = memo(({ id }: { id: string }) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
const item = useEntry(id, (state) => ({
|
const item = useEntry(id, (state) => ({
|
||||||
attachments: state.attachments,
|
attachments: state.attachments,
|
||||||
media: state.media,
|
media: state.media,
|
||||||
|
|
@ -49,7 +51,7 @@ export const EntryVideoItem = memo(({ id }: { id: string }) => {
|
||||||
entryId: id,
|
entryId: id,
|
||||||
})
|
})
|
||||||
if (!item.url) {
|
if (!item.url) {
|
||||||
toast.error("No video URL found")
|
toast.error(t("entry_content.no_video_url"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
openVideo(item.url)
|
openVideo(item.url)
|
||||||
|
|
@ -68,7 +70,7 @@ export const EntryVideoItem = memo(({ id }: { id: string }) => {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<FallbackMedia />
|
<FallbackMedia text={t("entry_content.no_content")} />
|
||||||
)}
|
)}
|
||||||
{!!duration && (
|
{!!duration && (
|
||||||
<Text className="absolute bottom-2 right-2 rounded-md bg-black/50 px-1 py-0.5 text-xs font-medium text-white">
|
<Text className="absolute bottom-2 right-2 rounded-md bg-black/50 px-1 py-0.5 text-xs font-medium text-white">
|
||||||
|
|
@ -83,14 +85,14 @@ export const EntryVideoItem = memo(({ id }: { id: string }) => {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
EntryVideoItem.displayName = "EntryVideoItem"
|
EntryVideoItem.displayName = "EntryVideoItem"
|
||||||
const FallbackMedia = () => (
|
const FallbackMedia = ({ text }: { text: string }) => (
|
||||||
<View
|
<View
|
||||||
className="w-full items-center justify-center rounded-lg bg-tertiary-system-fill"
|
className="w-full items-center justify-center rounded-lg bg-tertiary-system-fill"
|
||||||
style={{
|
style={{
|
||||||
aspectRatio: 16 / 9,
|
aspectRatio: 16 / 9,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Text className="text-center text-label">No media available</Text>
|
<Text className="text-center text-label">{text}</Text>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
const parseSchemeLink = (url: string) => {
|
const parseSchemeLink = (url: string) => {
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
import { tracker } from "@follow/tracker"
|
import { tracker } from "@follow/tracker"
|
||||||
import { zodResolver } from "@hookform/resolvers/zod"
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
import { useMutation } from "@tanstack/react-query"
|
import { useMutation } from "@tanstack/react-query"
|
||||||
|
import i18next from "i18next"
|
||||||
import { useCallback, useRef } from "react"
|
import { useCallback, useRef } from "react"
|
||||||
import type { Control } from "react-hook-form"
|
import type { Control } from "react-hook-form"
|
||||||
import { useController, useForm } from "react-hook-form"
|
import { useController, useForm } from "react-hook-form"
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
import type { TextInputProps } from "react-native"
|
import type { TextInputProps } from "react-native"
|
||||||
import { Alert, TouchableOpacity, View } from "react-native"
|
import { Alert, TouchableOpacity, View } from "react-native"
|
||||||
import { KeyboardController } from "react-native-keyboard-controller"
|
import { KeyboardController } from "react-native-keyboard-controller"
|
||||||
|
|
@ -30,7 +32,7 @@ async function onSubmit(values: FormValue) {
|
||||||
const result = formSchema.safeParse(values)
|
const result = formSchema.safeParse(values)
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
const issue = result.error.issues[0]
|
const issue = result.error.issues[0]
|
||||||
Alert.alert("Invalid email or password", issue?.message)
|
Alert.alert(i18next.t("login.invalid_email_or_password"), issue?.message)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
await signIn
|
await signIn
|
||||||
|
|
@ -60,6 +62,7 @@ async function onSubmit(values: FormValue) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
export function EmailLogin() {
|
export function EmailLogin() {
|
||||||
|
const { t } = useTranslation()
|
||||||
const emailValueRef = useRef("")
|
const emailValueRef = useRef("")
|
||||||
const passwordValueRef = useRef("")
|
const passwordValueRef = useRef("")
|
||||||
const submitMutation = useMutation({
|
const submitMutation = useMutation({
|
||||||
|
|
@ -86,7 +89,7 @@ export function EmailLogin() {
|
||||||
autoCorrect={false}
|
autoCorrect={false}
|
||||||
keyboardType="email-address"
|
keyboardType="email-address"
|
||||||
autoComplete="email"
|
autoComplete="email"
|
||||||
placeholder="Email"
|
placeholder={t("login.email")}
|
||||||
className="flex-1 text-text"
|
className="flex-1 text-text"
|
||||||
returnKeyType="next"
|
returnKeyType="next"
|
||||||
onSubmitEditing={() => {
|
onSubmitEditing={() => {
|
||||||
|
|
@ -105,7 +108,7 @@ export function EmailLogin() {
|
||||||
autoCapitalize="none"
|
autoCapitalize="none"
|
||||||
autoCorrect={false}
|
autoCorrect={false}
|
||||||
autoComplete="current-password"
|
autoComplete="current-password"
|
||||||
placeholder="Password"
|
placeholder={t("login.password")}
|
||||||
className="flex-1 text-text"
|
className="flex-1 text-text"
|
||||||
secureTextEntry
|
secureTextEntry
|
||||||
returnKeyType="go"
|
returnKeyType="go"
|
||||||
|
|
@ -118,9 +121,13 @@ export function EmailLogin() {
|
||||||
className="mx-auto my-5"
|
className="mx-auto my-5"
|
||||||
onPress={() => navigation.presentControllerView(ForgetPasswordScreen)}
|
onPress={() => navigation.presentControllerView(ForgetPasswordScreen)}
|
||||||
>
|
>
|
||||||
<Text className="text-sm text-secondary-label">Forgot password?</Text>
|
<Text className="text-sm text-secondary-label">{t("login.forget_password.note")}</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<SubmitButton isLoading={submitMutation.isPending} onPress={onLogin} title="Continue" />
|
<SubmitButton
|
||||||
|
isLoading={submitMutation.isPending}
|
||||||
|
onPress={onLogin}
|
||||||
|
title={t("login.submit")}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -134,7 +141,7 @@ const signupFormSchema = z
|
||||||
confirmPassword: z.string(),
|
confirmPassword: z.string(),
|
||||||
})
|
})
|
||||||
.refine((data) => data.password === data.confirmPassword, {
|
.refine((data) => data.password === data.confirmPassword, {
|
||||||
message: "Passwords don't match",
|
message: i18next.t("login.passwords_do_not_match"),
|
||||||
path: ["confirmPassword"],
|
path: ["confirmPassword"],
|
||||||
})
|
})
|
||||||
type SignupFormValue = z.infer<typeof signupFormSchema>
|
type SignupFormValue = z.infer<typeof signupFormSchema>
|
||||||
|
|
@ -160,6 +167,7 @@ function SignupInput({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function EmailSignUp() {
|
export function EmailSignUp() {
|
||||||
|
const { t } = useTranslation()
|
||||||
const { control, handleSubmit, formState } = useForm<SignupFormValue>({
|
const { control, handleSubmit, formState } = useForm<SignupFormValue>({
|
||||||
resolver: zodResolver(signupFormSchema),
|
resolver: zodResolver(signupFormSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
|
|
@ -185,7 +193,7 @@ export function EmailSignUp() {
|
||||||
if (res.error?.message) {
|
if (res.error?.message) {
|
||||||
toast.error(res.error.message)
|
toast.error(res.error.message)
|
||||||
} else {
|
} else {
|
||||||
toast.success("Sign up successful")
|
toast.success(i18next.t("login.sign_up_successful"))
|
||||||
tracker.register({
|
tracker.register({
|
||||||
type: "email",
|
type: "email",
|
||||||
})
|
})
|
||||||
|
|
@ -209,7 +217,7 @@ export function EmailSignUp() {
|
||||||
autoComplete="email"
|
autoComplete="email"
|
||||||
control={control}
|
control={control}
|
||||||
name="email"
|
name="email"
|
||||||
placeholder="Email"
|
placeholder={t("login.email")}
|
||||||
className="flex-1 text-text"
|
className="flex-1 text-text"
|
||||||
returnKeyType="next"
|
returnKeyType="next"
|
||||||
onSubmitEditing={() => {
|
onSubmitEditing={() => {
|
||||||
|
|
@ -226,7 +234,7 @@ export function EmailSignUp() {
|
||||||
autoComplete="password-new"
|
autoComplete="password-new"
|
||||||
control={control}
|
control={control}
|
||||||
name="password"
|
name="password"
|
||||||
placeholder="Password"
|
placeholder={t("login.password")}
|
||||||
className="flex-1 text-text"
|
className="flex-1 text-text"
|
||||||
secureTextEntry
|
secureTextEntry
|
||||||
returnKeyType="next"
|
returnKeyType="next"
|
||||||
|
|
@ -241,7 +249,7 @@ export function EmailSignUp() {
|
||||||
autoComplete="password-new"
|
autoComplete="password-new"
|
||||||
control={control}
|
control={control}
|
||||||
name="confirmPassword"
|
name="confirmPassword"
|
||||||
placeholder="Confirm Password"
|
placeholder={t("login.confirm_password.label")}
|
||||||
className="flex-1 text-text"
|
className="flex-1 text-text"
|
||||||
secureTextEntry
|
secureTextEntry
|
||||||
returnKeyType="go"
|
returnKeyType="go"
|
||||||
|
|
@ -255,7 +263,7 @@ export function EmailSignUp() {
|
||||||
disabled={submitMutation.isPending || !formState.isValid}
|
disabled={submitMutation.isPending || !formState.isValid}
|
||||||
isLoading={submitMutation.isPending}
|
isLoading={submitMutation.isPending}
|
||||||
onPress={signup}
|
onPress={signup}
|
||||||
title="Continue"
|
title={t("login.submit")}
|
||||||
className="mt-8"
|
className="mt-8"
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
|
|
||||||
|
|
@ -114,24 +114,23 @@ const TermsCheckBox = () => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
const TermsText = () => {
|
const TermsText = () => {
|
||||||
|
const { t } = useTranslation()
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
<Text className="text-center text-sm text-secondary-label">
|
<Text className="text-center text-sm text-secondary-label">{t("login.agree_to")} </Text>
|
||||||
By continuing, you agree to our{" "}
|
|
||||||
</Text>
|
|
||||||
<View className="flex-row items-center">
|
<View className="flex-row items-center">
|
||||||
<Pressable
|
<Pressable
|
||||||
onPress={() => Linking.openURL("https://folo.is/terms-of-service")}
|
onPress={() => Linking.openURL("https://folo.is/terms-of-service")}
|
||||||
className="text-secondary-label"
|
className="text-secondary-label"
|
||||||
>
|
>
|
||||||
<Text className="font-semibold">Terms of Service</Text>
|
<Text className="font-semibold">{t("login.terms")}</Text>
|
||||||
</Pressable>
|
</Pressable>
|
||||||
<Text className="text-secondary-label"> & </Text>
|
<Text className="text-secondary-label"> & </Text>
|
||||||
<Pressable
|
<Pressable
|
||||||
onPress={() => Linking.openURL("https://folo.is/privacy-policy")}
|
onPress={() => Linking.openURL("https://folo.is/privacy-policy")}
|
||||||
className="text-secondary-label"
|
className="text-secondary-label"
|
||||||
>
|
>
|
||||||
<Text className="font-semibold">Privacy Policy</Text>
|
<Text className="font-semibold">{t("login.privacy")}</Text>
|
||||||
</Pressable>
|
</Pressable>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,9 @@ export function LanguageSelect({ settingKey }: { settingKey: "language" | "actio
|
||||||
const languageKeys =
|
const languageKeys =
|
||||||
settingKey === "language"
|
settingKey === "language"
|
||||||
? (currentSupportedLanguages as MobileSupportedLanguages[])
|
? (currentSupportedLanguages as MobileSupportedLanguages[])
|
||||||
: ACTION_LANGUAGE_KEYS.sort(
|
: ACTION_LANGUAGE_KEYS.filter(
|
||||||
|
(key): key is MobileSupportedLanguages => key in defaultResources,
|
||||||
|
).sort(
|
||||||
(a, b) => currentSupportedLanguages.indexOf(a) - currentSupportedLanguages.indexOf(b),
|
(a, b) => currentSupportedLanguages.indexOf(a) - currentSupportedLanguages.indexOf(b),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import {
|
||||||
|
|
||||||
export const DiscoverSettingsScreen = () => {
|
export const DiscoverSettingsScreen = () => {
|
||||||
const { t } = useTranslation("settings")
|
const { t } = useTranslation("settings")
|
||||||
|
const { t: tCommon } = useTranslation("common")
|
||||||
const discoverLanguage = useUISettingKey("discoverLanguage")
|
const discoverLanguage = useUISettingKey("discoverLanguage")
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -25,16 +26,17 @@ export const DiscoverSettingsScreen = () => {
|
||||||
<GroupedInsetListSectionHeader label={t("discoverFilters.filters")} marginSize="small" />
|
<GroupedInsetListSectionHeader label={t("discoverFilters.filters")} marginSize="small" />
|
||||||
<GroupedInsetListCard className="flex-row">
|
<GroupedInsetListCard className="flex-row">
|
||||||
<GroupedInsetListCell label={t("discoverFilters.language")}>
|
<GroupedInsetListCell label={t("discoverFilters.language")}>
|
||||||
<View className="w-[100px]">
|
<View className="w-[120px]">
|
||||||
<Select
|
<Select
|
||||||
options={[
|
options={[
|
||||||
{ label: "All", value: "all" },
|
{ label: tCommon("words.all"), value: "all" },
|
||||||
{ label: "English", value: "eng" },
|
{ label: tCommon("words.english"), value: "eng" },
|
||||||
{ label: "中文", value: "cmn" },
|
{ label: tCommon("words.chinese"), value: "cmn" },
|
||||||
|
{ label: tCommon("words.french"), value: "fra" },
|
||||||
]}
|
]}
|
||||||
value={discoverLanguage}
|
value={discoverLanguage}
|
||||||
onValueChange={(val) => {
|
onValueChange={(val) => {
|
||||||
setUISetting("discoverLanguage", val as "all" | "eng" | "cmn")
|
setUISetting("discoverLanguage", val as "all" | "eng" | "cmn" | "fra")
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,371 @@
|
||||||
|
{
|
||||||
|
"ai_summary": "Résumé IA",
|
||||||
|
"analytics.chart_placeholder": "La visualisation graphique sera implémentée avec recharts",
|
||||||
|
"analytics.efficiency_analysis": "Analyse d'efficacité",
|
||||||
|
"analytics.efficiency_placeholder": "Comparaison de l'efficacité du modèle et suggestions d'optimisation",
|
||||||
|
"analytics.history_operation": "Opération {{operation}}",
|
||||||
|
"analytics.history_usage": "Utilisation des crédits IA",
|
||||||
|
"analytics.no_data": "Aucune donnée d'utilisation disponible pour la période sélectionnée",
|
||||||
|
"analytics.no_history": "Aucun historique d'utilisation disponible",
|
||||||
|
"analytics.operation_types.chat": "Discussion",
|
||||||
|
"analytics.operation_types.onboarding": "Intégration",
|
||||||
|
"analytics.operation_types.task": "Tâche",
|
||||||
|
"analytics.operation_types.title_generation": "Génération de titre",
|
||||||
|
"analytics.patterns_placeholder": "L'analyse des modèles montrera les heures de pointe et les habitudes",
|
||||||
|
"analytics.tabs.efficiency": "Efficacité",
|
||||||
|
"analytics.tabs.history": "Historique",
|
||||||
|
"analytics.tabs.overview": "Aperçu",
|
||||||
|
"analytics.tabs.patterns": "Modèles",
|
||||||
|
"analytics.trend.operations": "Opérations",
|
||||||
|
"analytics.trend.title": "Tendances d'utilisation (30 derniers jours)",
|
||||||
|
"analytics.trend.tokens": "Crédits utilisés",
|
||||||
|
"analytics.usage_history": "Historique d'utilisation",
|
||||||
|
"analytics.usage_patterns": "Modèles d'utilisation",
|
||||||
|
"analytics.usage_trends": "Tendances d'utilisation (30 derniers jours)",
|
||||||
|
"byok.description": "Utilisez vos propres clés API pour les fournisseurs d'IA afin d'avoir plus de contrôle sur les coûts et l'utilisation.",
|
||||||
|
"byok.enabled": "Activer BYOK",
|
||||||
|
"byok.providers.add": "Ajouter un fournisseur",
|
||||||
|
"byok.providers.add_title": "Ajouter un fournisseur BYOK",
|
||||||
|
"byok.providers.added": "Fournisseur ajouté avec succès",
|
||||||
|
"byok.providers.configured": "Configuré",
|
||||||
|
"byok.providers.delete": "Supprimer le fournisseur",
|
||||||
|
"byok.providers.delete_message": "Êtes-vous sûr de vouloir supprimer ce fournisseur ? Cette action est irréversible.",
|
||||||
|
"byok.providers.delete_title": "Supprimer le fournisseur",
|
||||||
|
"byok.providers.deleted": "Fournisseur supprimé avec succès",
|
||||||
|
"byok.providers.edit": "Modifier le fournisseur",
|
||||||
|
"byok.providers.edit_title": "Modifier le fournisseur BYOK",
|
||||||
|
"byok.providers.empty.description": "Ajoutez vos propres clés API pour les fournisseurs d'IA afin d'utiliser votre propre infrastructure.",
|
||||||
|
"byok.providers.empty.title": "Aucun fournisseur configuré",
|
||||||
|
"byok.providers.form.api_key": "Clé API",
|
||||||
|
"byok.providers.form.api_key_help": "Votre clé API sera stockée en toute sécurité et chiffrée.",
|
||||||
|
"byok.providers.form.api_key_placeholder": "Entrez votre clé API",
|
||||||
|
"byok.providers.form.base_url": "URL de base",
|
||||||
|
"byok.providers.form.base_url_help": "Optionnel. Laissez vide pour utiliser le point de terminaison API par défaut.",
|
||||||
|
"byok.providers.form.base_url_placeholder": "ex. https://api.openai.com/v1",
|
||||||
|
"byok.providers.form.provider": "Fournisseur",
|
||||||
|
"byok.providers.headers_count": "en-tête(s)",
|
||||||
|
"byok.providers.no_api_key": "Aucune clé API définie",
|
||||||
|
"byok.providers.title": "Fournisseurs",
|
||||||
|
"byok.providers.updated": "Fournisseur mis à jour avec succès",
|
||||||
|
"byok.title": "Bring Your Own Key (BYOK)",
|
||||||
|
"chat.history.auto_title": "Discussion du {{- datetime}}",
|
||||||
|
"clear_chat": "Effacer la discussion",
|
||||||
|
"clear_chat_message": "Êtes-vous sûr de vouloir effacer tout l'historique ? Cette action supprimera définitivement tout le contenu, y compris tous les journaux de discussion et les données, et est irréversible.",
|
||||||
|
"common.generate_title": "Générer un titre",
|
||||||
|
"common.generating_title": "Génération du titre...",
|
||||||
|
"common.new_chat": "Nouvelle discussion",
|
||||||
|
"customize_shortcuts": "Personnaliser les raccourcis",
|
||||||
|
"delete_chat": "Supprimer la discussion",
|
||||||
|
"delete_chat_error": "Échec de la suppression de la discussion",
|
||||||
|
"delete_chat_message": "Êtes-vous sûr de vouloir supprimer \"{{title}}\" ? Cette action est irréversible.",
|
||||||
|
"delete_chat_success": "Discussion supprimée avec succès",
|
||||||
|
"features.title": "Fonctionnalités",
|
||||||
|
"integration.mcp.description": "Connectez-vous aux services compatibles MCP qui étendent les capacités de l'IA via une intégration OAuth sécurisée.",
|
||||||
|
"integration.mcp.enabled": "Activer les services MCP",
|
||||||
|
"integration.mcp.security.description": "Les jetons d'authentification OAuth sont stockés en toute sécurité à l'aide du chiffrement AES-GCM-256 pour protéger vos informations d'identification et garantir la confidentialité des données.",
|
||||||
|
"integration.mcp.security.title": "Divulgation de sécurité",
|
||||||
|
"integration.mcp.service.active": "Actif",
|
||||||
|
"integration.mcp.service.added": "Service MCP ajouté avec succès",
|
||||||
|
"integration.mcp.service.auth_message": "Ce service nécessite une autorisation OAuth. Cliquez sur 'Ouvrir l'autorisation' pour procéder à l'authentification dans une nouvelle fenêtre.",
|
||||||
|
"integration.mcp.service.auth_required": "Autorisation requise",
|
||||||
|
"integration.mcp.service.auth_window_opened": "Fenêtre d'autorisation ouverte. Veuillez terminer l'authentification.",
|
||||||
|
"integration.mcp.service.baseUrl": "URL de base du service",
|
||||||
|
"integration.mcp.service.baseUrl_placeholder": "ex. https://api.emple.com",
|
||||||
|
"integration.mcp.service.connect": "Connecter",
|
||||||
|
"integration.mcp.service.connected": "Connecté",
|
||||||
|
"integration.mcp.service.connected_success": "Connecté avec succès au service MCP",
|
||||||
|
"integration.mcp.service.connecting": "Connexion en cours...",
|
||||||
|
"integration.mcp.service.connection_failed": "Échec de la connexion au service MCP",
|
||||||
|
"integration.mcp.service.degraded": "Dégradé",
|
||||||
|
"integration.mcp.service.delete_message": "Êtes-vous sûr de vouloir supprimer ce service MCP ? Cette action est irréversible.",
|
||||||
|
"integration.mcp.service.delete_title": "Supprimer le service MCP",
|
||||||
|
"integration.mcp.service.deleted": "Service MCP supprimé avec succès",
|
||||||
|
"integration.mcp.service.disabled": "Désactivé",
|
||||||
|
"integration.mcp.service.disconnect": "Déconnecter",
|
||||||
|
"integration.mcp.service.disconnected": "Déconnecté",
|
||||||
|
"integration.mcp.service.discover": "Découvrir le service",
|
||||||
|
"integration.mcp.service.discovery_failed": "Échec de la découverte des points de terminaison du service",
|
||||||
|
"integration.mcp.service.enabled": "Activé",
|
||||||
|
"integration.mcp.service.endpoints": "Points de terminaison OAuth",
|
||||||
|
"integration.mcp.service.error": "Erreur de connexion",
|
||||||
|
"integration.mcp.service.healthy": "Sain",
|
||||||
|
"integration.mcp.service.inactive": "Inactif",
|
||||||
|
"integration.mcp.service.name": "Nom du service",
|
||||||
|
"integration.mcp.service.name_placeholder": "ex. Outils GitHub, Intégration Slack",
|
||||||
|
"integration.mcp.service.open_auth": "Ouvrir l'autorisation",
|
||||||
|
"integration.mcp.service.open_manually": "Ouvrir l'autorisation",
|
||||||
|
"integration.mcp.service.popup_blocked": "La fenêtre contextuelle d'autorisation a été bloquée par votre navigateur",
|
||||||
|
"integration.mcp.service.reconnect": "Reconnecter",
|
||||||
|
"integration.mcp.service.scopes": "Champs requis",
|
||||||
|
"integration.mcp.service.unhealthy": "Malsain",
|
||||||
|
"integration.mcp.service.updated": "Service MCP mis à jour avec succès",
|
||||||
|
"integration.mcp.service.validation.baseUrl_required": "L'URL de base est requise",
|
||||||
|
"integration.mcp.service.validation.invalid_url": "Veuillez entrer une URL valide",
|
||||||
|
"integration.mcp.service.validation.name_required": "Le nom du service est requis",
|
||||||
|
"integration.mcp.services.add": "Ajouter un service",
|
||||||
|
"integration.mcp.services.empty.description": "Connectez des services MCP pour étendre les capacités de l'IA avec des outils externes, des API et des sources de données.",
|
||||||
|
"integration.mcp.services.empty.title": "Aucun service MCP connecté",
|
||||||
|
"integration.mcp.services.title": "Services MCP",
|
||||||
|
"integration.security.description": "Les jetons d'authentification OAuth et les clés API AI BYOK sont stockés en toute sécurité à l'aide du chiffrement AES-GCM-256 pour protéger vos informations d'identification et garantir la confidentialité des données.",
|
||||||
|
"integration.security.title": "Divulgation de sécurité",
|
||||||
|
"integration.title": "Intégration",
|
||||||
|
"memories.actions.load_more": "Charger plus",
|
||||||
|
"memories.actions.loading": "Chargement...",
|
||||||
|
"memories.importance": "Importance",
|
||||||
|
"memories.list.description": "L'IA apprend à vous connaître au travers des conversations. Ces souvenirs l'aident à mieux vous comprendre au fil du temps.",
|
||||||
|
"memories.list.empty.description": "Au fur et à mesure que vous discutez avec l'IA, elle apprendra progressivement vos préférences et vos habitudes. Les souvenirs apparaîtront ici.",
|
||||||
|
"memories.list.empty.title": "Aucun souvenir pour le moment",
|
||||||
|
"memories.section.title": "Mémoire de l'IA sur vous",
|
||||||
|
"memories.toast.deleted": "Souvenir supprimé",
|
||||||
|
"memories.toast.failed": "Échec de l'enregistrement du souvenir",
|
||||||
|
"memories.toast.processing": "Traitement...",
|
||||||
|
"memories.toast.saved": "Souvenir enregistré",
|
||||||
|
"memories.toast.saving": "Enregistrement du souvenir...",
|
||||||
|
"memories.toast.updated": "Souvenir mis à jour",
|
||||||
|
"memories.toast.updating": "Mise à jour du souvenir...",
|
||||||
|
"mentions.date.relative.last_15_days.label": "15 derniers jours",
|
||||||
|
"mentions.date.relative.last_15_days.search": "15 derniers jours|15 jours passés|dernier demi mois|demi mois passé",
|
||||||
|
"mentions.date.relative.last_30_days.label": "30 derniers jours",
|
||||||
|
"mentions.date.relative.last_30_days.search": "30 derniers jours|30 jours passés",
|
||||||
|
"mentions.date.relative.last_3_days.label": "3 derniers jours",
|
||||||
|
"mentions.date.relative.last_3_days.search": "3 derniers jours|3 jours passés",
|
||||||
|
"mentions.date.relative.last_7_days.label": "7 derniers jours",
|
||||||
|
"mentions.date.relative.last_7_days.search": "7 derniers jours|7 jours passés",
|
||||||
|
"mentions.date.relative.last_month.label": "Le mois dernier",
|
||||||
|
"mentions.date.relative.last_month.search": "mois dernier|mois précédent",
|
||||||
|
"mentions.date.relative.last_week.label": "La semaine dernière",
|
||||||
|
"mentions.date.relative.last_week.search": "semaine dernière|semaine précédente",
|
||||||
|
"mentions.date.relative.this_month.label": "Ce mois-ci",
|
||||||
|
"mentions.date.relative.this_month.search": "ce mois|mois actuel",
|
||||||
|
"mentions.date.relative.this_week.label": "Cette semaine",
|
||||||
|
"mentions.date.relative.this_week.search": "cette semaine|semaine actuelle",
|
||||||
|
"mentions.date.relative.today.label": "Aujourd'hui",
|
||||||
|
"mentions.date.relative.today.search": "aujourd'hui",
|
||||||
|
"mentions.date.relative.yesterday.label": "Hier",
|
||||||
|
"mentions.date.relative.yesterday.search": "hier",
|
||||||
|
"mentions.date.weekday.auto.label": "{{weekday}}",
|
||||||
|
"mentions.date.weekday.day.friday.label": "Vendredi",
|
||||||
|
"mentions.date.weekday.day.friday.search": "vendredi|ven",
|
||||||
|
"mentions.date.weekday.day.monday.label": "Lundi",
|
||||||
|
"mentions.date.weekday.day.monday.search": "lundi|lun",
|
||||||
|
"mentions.date.weekday.day.saturday.label": "Samedi",
|
||||||
|
"mentions.date.weekday.day.saturday.search": "samedi|sam",
|
||||||
|
"mentions.date.weekday.day.sunday.label": "Dimanche",
|
||||||
|
"mentions.date.weekday.day.sunday.search": "dimanche|dim",
|
||||||
|
"mentions.date.weekday.day.thursday.label": "Jeudi",
|
||||||
|
"mentions.date.weekday.day.thursday.search": "jeudi|jeu",
|
||||||
|
"mentions.date.weekday.day.tuesday.label": "Mardi",
|
||||||
|
"mentions.date.weekday.day.tuesday.search": "mardi|mar",
|
||||||
|
"mentions.date.weekday.day.wednesday.label": "Mercredi",
|
||||||
|
"mentions.date.weekday.day.wednesday.search": "mercredi|mer",
|
||||||
|
"mentions.date.weekday.last.label": "{{weekday}} dernier",
|
||||||
|
"mentions.date.weekday.prefix.auto.search": "",
|
||||||
|
"mentions.date.weekday.prefix.last.search": "dernier|semaine dernière",
|
||||||
|
"mentions.date.weekday.prefix.this.search": "ce|cette semaine",
|
||||||
|
"mentions.date.weekday.this.label": "Ce {{weekday}}",
|
||||||
|
"mentions.section.category": "Catégories",
|
||||||
|
"mentions.section.date": "Dates",
|
||||||
|
"mentions.section.entry": "Entrées",
|
||||||
|
"mentions.section.feed": "Flux",
|
||||||
|
"mentions.section.view": "Vues",
|
||||||
|
"new_shortcuts": "Nouveau raccourci",
|
||||||
|
"personalize.description": "Parlez-moi de vous pour obtenir des réponses IA personnalisées.",
|
||||||
|
"personalize.prompt.help": "Cela aide l'IA à fournir des réponses personnalisées en fonction de vos préférences.",
|
||||||
|
"personalize.prompt.placeholder": "Parlez-moi de vous et de la façon dont vous préférez lire le contenu...",
|
||||||
|
"personalize.saved": "Personnalisation enregistrée avec succès",
|
||||||
|
"personalize.title": "Personnalisation",
|
||||||
|
"quick_actions.discuss": "Discuter des idées",
|
||||||
|
"quick_actions.questions": "Poser des questions",
|
||||||
|
"quick_actions.simplify": "Simplifier ceci",
|
||||||
|
"quick_actions.takeaways": "Points clés à retenir",
|
||||||
|
"rate_limit.credits_left": "{{count}} crédits restants",
|
||||||
|
"rate_limit.depleted": "Crédits IA épuisés",
|
||||||
|
"rate_limit.minute": "minute",
|
||||||
|
"rate_limit.minutes": "minutes",
|
||||||
|
"rate_limit.resets_at": "réinitialisé à {{time}}",
|
||||||
|
"rate_limit.resets_in": "réinitialisé dans {{value}}",
|
||||||
|
"rate_limit.resets_next_month": "réinitialisé le mois prochain",
|
||||||
|
"rate_limit.resets_tomorrow": "réinitialisé demain",
|
||||||
|
"rate_limit.upgrade_plan_button": "Essai gratuit",
|
||||||
|
"rate_limit.upgrade_to_get_more": "Commencez l'essai gratuit pour obtenir plus de crédits IA.",
|
||||||
|
"schedule.configuration_label": "Configuration",
|
||||||
|
"schedule.date_time_label": "Date et heure",
|
||||||
|
"schedule.date_time_placeholder": "Sélectionner la date et l'heure",
|
||||||
|
"schedule.day_label": "Jour",
|
||||||
|
"schedule.day_placeholder": "Sélectionner le jour",
|
||||||
|
"schedule.days.friday": "Vendredi",
|
||||||
|
"schedule.days.monday": "Lundi",
|
||||||
|
"schedule.days.saturday": "Samedi",
|
||||||
|
"schedule.days.sunday": "Dimanche",
|
||||||
|
"schedule.days.thursday": "Jeudi",
|
||||||
|
"schedule.days.tuesday": "Mardi",
|
||||||
|
"schedule.days.wednesday": "Mercredi",
|
||||||
|
"schedule.frequency.daily": "Quotidien",
|
||||||
|
"schedule.frequency.monthly": "Mensuel",
|
||||||
|
"schedule.frequency.once": "Une fois",
|
||||||
|
"schedule.frequency.weekly": "Hebdomadaire",
|
||||||
|
"schedule.next_execution": "Prochain : {{time}} ({{relative}})",
|
||||||
|
"schedule.no_upcoming": "Aucune exécution à venir",
|
||||||
|
"schedule.presets.daily_6pm": "Quotidien 18h",
|
||||||
|
"schedule.presets.first_9am": "1er 9h",
|
||||||
|
"schedule.presets.monday_9am": "Lun 9h",
|
||||||
|
"schedule.presets.tomorrow_9am": "Demain 9h",
|
||||||
|
"schedule.presets_title": "Préréglages rapides",
|
||||||
|
"schedule.time_label": "Heure",
|
||||||
|
"schedule.time_placeholder": "Sélectionner l'heure",
|
||||||
|
"schedule.title": "Calendrier",
|
||||||
|
"session.interrupted.message": "Nous avons perdu la connexion avant que l'assistant ne réponde. Réessayez votre dernier message.",
|
||||||
|
"session.interrupted.retry": "Réessayer",
|
||||||
|
"settings.autoScrollWhenStreaming.description": "Faire défiler automatiquement vers le bas de la discussion lors du streaming.",
|
||||||
|
"settings.autoScrollWhenStreaming.label": "Défilement auto. lors du streaming",
|
||||||
|
"settings.description": "Configurez votre expérience IA et créez des raccourcis personnalisés.",
|
||||||
|
"settings.panel_style.description": "Le style du panneau de discussion IA.",
|
||||||
|
"settings.panel_style.fixed": "Fixe",
|
||||||
|
"settings.panel_style.floating": "Flottant",
|
||||||
|
"settings.panel_style.label": "Style de panneau",
|
||||||
|
"settings.showSplineButton.description": "Afficher ou masquer l'indicateur d'assistant IA dans le coin inférieur droit.",
|
||||||
|
"settings.showSplineButton.label": "Indicateur d'assistant IA",
|
||||||
|
"settings.title": "Paramètres IA",
|
||||||
|
"shortcuts.actions.delete": "Supprimer le raccourci",
|
||||||
|
"shortcuts.actions.disable": "Désactiver le raccourci",
|
||||||
|
"shortcuts.actions.edit": "Modifier le raccourci",
|
||||||
|
"shortcuts.add": "Ajouter un raccourci",
|
||||||
|
"shortcuts.added": "Raccourci ajouté avec succès",
|
||||||
|
"shortcuts.context_menu.empty.entry": "Aucun raccourci d'entrée configuré pour le moment",
|
||||||
|
"shortcuts.context_menu.empty.list": "Aucun raccourci disponible pour la vue liste pour le moment",
|
||||||
|
"shortcuts.create_first": "Créez votre premier raccourci",
|
||||||
|
"shortcuts.custom_prompt.help": "Laissez vide pour utiliser l'invite par défaut fournie par le serveur.",
|
||||||
|
"shortcuts.custom_prompt.title": "Invite personnalisée",
|
||||||
|
"shortcuts.custom_prompt_placeholder": "Remplacer l'invite par défaut...",
|
||||||
|
"shortcuts.default_prompt.label": "Invite par défaut",
|
||||||
|
"shortcuts.deleted": "Raccourci supprimé avec succès",
|
||||||
|
"shortcuts.empty.description": "Créez des raccourcis IA personnalisés pour effectuer rapidement des tâches courantes et obtenir une assistance IA instantanée.",
|
||||||
|
"shortcuts.empty.title": "Aucun raccourci pour le moment",
|
||||||
|
"shortcuts.enabled": "Activé",
|
||||||
|
"shortcuts.icon": "Icône",
|
||||||
|
"shortcuts.manage": "Gérer les raccourcis",
|
||||||
|
"shortcuts.name": "Nom",
|
||||||
|
"shortcuts.name_placeholder": "ex. Résumer l'article",
|
||||||
|
"shortcuts.prompt": "Invite",
|
||||||
|
"shortcuts.prompt_placeholder": "Écrivez une instruction claire pour l'IA...",
|
||||||
|
"shortcuts.server_delete_disabled": "Les raccourcis fournis par le serveur ne peuvent pas être supprimés",
|
||||||
|
"shortcuts.targets.entry": "Détails de l'entrée",
|
||||||
|
"shortcuts.targets.help": "Choisissez des scénarios pour contrôler où ce raccourci apparaît.",
|
||||||
|
"shortcuts.targets.label": "Lieu d'affichage",
|
||||||
|
"shortcuts.targets.list": "Chronologie",
|
||||||
|
"shortcuts.title": "Raccourcis IA",
|
||||||
|
"shortcuts.updated": "Raccourci mis à jour avec succès",
|
||||||
|
"shortcuts.validation.name_required": "Le nom est requis",
|
||||||
|
"shortcuts.validation.prompt_required": "Une invite est requise",
|
||||||
|
"shortcuts.validation.required": "Le nom et l'invite sont requis",
|
||||||
|
"shortcuts.validation.targets_required": "Sélectionnez au moins un lieu d'affichage",
|
||||||
|
"summary_not_available": "Résumé non disponible",
|
||||||
|
"tasks.actions.delete_task": "Supprimer la tâche",
|
||||||
|
"tasks.actions.edit_task": "Modifier la tâche",
|
||||||
|
"tasks.actions.new_task": "Nouvelle tâche",
|
||||||
|
"tasks.actions.schedule": "Planifier la tâche",
|
||||||
|
"tasks.actions.scheduling": "Planification...",
|
||||||
|
"tasks.actions.test_run": "Test",
|
||||||
|
"tasks.actions.update": "Mettre à jour la tâche",
|
||||||
|
"tasks.actions.updating": "Mise à jour...",
|
||||||
|
"tasks.actions.view_reports": "Voir les rapports",
|
||||||
|
"tasks.empty.desc": "Créez votre première tâche IA pour automatiser vos flux de travail.",
|
||||||
|
"tasks.empty.title": "Aucune tâche planifiée",
|
||||||
|
"tasks.fields.created": "Créé :",
|
||||||
|
"tasks.fields.prompt": "Invite :",
|
||||||
|
"tasks.fields.schedule": "Calendrier :",
|
||||||
|
"tasks.manage.desc": "Créez et gérez des tâches IA automatisées qui s'exécutent selon votre calendrier.",
|
||||||
|
"tasks.manage.limit_reached": "(Limite atteinte : nombre maximum de tâches atteint)",
|
||||||
|
"tasks.manage.title": "Planifier des tâches IA",
|
||||||
|
"tasks.modal.delete_confirm": "Êtes-vous sûr de vouloir supprimer la tâche \"{{name}}\" ?",
|
||||||
|
"tasks.modal.delete_title": "Supprimer la tâche",
|
||||||
|
"tasks.modal.edit_title": "Modifier la tâche IA",
|
||||||
|
"tasks.modal.new_title": "Nouvelle tâche IA",
|
||||||
|
"tasks.name": "Nom de la tâche",
|
||||||
|
"tasks.name_placeholder": "Entrez un nom descriptif pour votre tâche...",
|
||||||
|
"tasks.notify.coming_soon": "Bientôt disponible",
|
||||||
|
"tasks.notify.email": "Email",
|
||||||
|
"tasks.notify.email_helper": "Envoyer le résultat de la tâche à l'email de votre compte lorsqu'elle se termine.",
|
||||||
|
"tasks.prompt": "Invite",
|
||||||
|
"tasks.prompt_helper": "Fournissez des instructions claires et précises pour l'exécution de l'IA",
|
||||||
|
"tasks.prompt_placeholder": "Décrivez ce que vous voulez que l'IA fasse lorsque cette tâche s'exécute...",
|
||||||
|
"tasks.schedule.daily": "Quotidien à {{time}}",
|
||||||
|
"tasks.schedule.monthly": "Mensuel le {{day}} à {{time}}",
|
||||||
|
"tasks.schedule.once": "Une fois le {{date}} à {{time}}",
|
||||||
|
"tasks.schedule.unknown": "Calendrier inconnu",
|
||||||
|
"tasks.schedule.weekly": "Hebdomadaire le {{day}} à {{time}}",
|
||||||
|
"tasks.section.info": "Informations sur la tâche",
|
||||||
|
"tasks.section.instructions": "Instructions IA",
|
||||||
|
"tasks.section.notifications": "Notifications",
|
||||||
|
"tasks.section.schedule": "Configuration du calendrier",
|
||||||
|
"tasks.section.title": "Tâches IA",
|
||||||
|
"tasks.status.completed": "Terminé",
|
||||||
|
"tasks.status.paused": "En pause",
|
||||||
|
"tasks.status.scheduled": "Planifié",
|
||||||
|
"tasks.status.unknown": "Inconnu",
|
||||||
|
"tasks.toast.create_error": "Échec de la planification de la tâche IA. Veuillez réessayer.",
|
||||||
|
"tasks.toast.created": "Tâche IA planifiée avec succès",
|
||||||
|
"tasks.toast.delete_failed": "Échec de la suppression de la tâche. Veuillez réessayer.",
|
||||||
|
"tasks.toast.delete_success": "Tâche supprimée avec succès",
|
||||||
|
"tasks.toast.load_failed": "Échec du chargement des messages de discussion",
|
||||||
|
"tasks.toast.no_report": "Aucune session de rapport trouvée pour cette tâche pour le moment.",
|
||||||
|
"tasks.toast.switch_to_chat": "Passez au panneau de discussion pour voir les rapports.",
|
||||||
|
"tasks.toast.test_failed": "Échec de l'exécution du test. Veuillez réessayer.",
|
||||||
|
"tasks.toast.test_start": "Exécution...",
|
||||||
|
"tasks.toast.test_success": "Le test a réussi. Vous pouvez passer au panneau de discussion pour voir le rapport.",
|
||||||
|
"tasks.toast.update_error": "Échec de la mise à jour de la tâche IA. Veuillez réessayer.",
|
||||||
|
"tasks.toast.update_failed": "Échec de la mise à jour de la tâche. Veuillez réessayer.",
|
||||||
|
"tasks.toast.updated": "Tâche IA mise à jour avec succès",
|
||||||
|
"tasks.validation.date_future": "La date planifiée doit être dans le futur",
|
||||||
|
"tasks.validation.prompt_max": "L'invite doit comporter moins de 2000 caractères",
|
||||||
|
"tasks.validation.prompt_required": "L'invite est requise",
|
||||||
|
"tasks.validation.title_max": "Le titre doit comporter moins de 50 caractères",
|
||||||
|
"tasks.validation.title_required": "Le titre est requis",
|
||||||
|
"tasks.view_in_settings": "Voir les tâches planifiées",
|
||||||
|
"timeline.summary.title_template": "Résumé de la chronologie du {{- datetime}}",
|
||||||
|
"timeline_prompt.prompt.help": "Utilisé lorsque l'IA trie votre chronologie. Mentionnez les sujets ou les sources que vous préférez ainsi que tout ce que vous n'aimez pas.",
|
||||||
|
"timeline_prompt.prompt.placeholder": "Décrivez ce que vous voulez que la chronologie IA privilégie ou évite...",
|
||||||
|
"timeline_prompt.saved": "Préférences de tri de la chronologie enregistrées avec succès",
|
||||||
|
"timeline_prompt.title": "Invite de tri de la chronologie",
|
||||||
|
"timeline_summary.empty": "Le résumé de la chronologie apparaîtra ici sous peu.",
|
||||||
|
"timeline_summary.error": "Impossible de générer le résumé de la chronologie pour le moment.",
|
||||||
|
"timeline_summary.generating": "Résumé de votre chronologie...",
|
||||||
|
"timeline_summary.heading": "Quoi de neuf dans la chronologie",
|
||||||
|
"timeline_summary.options.include": "Discuter avec le résumé de la chronologie",
|
||||||
|
"token_usage.description": "Surveillez votre consommation de crédits IA et vos limites.",
|
||||||
|
"token_usage.resets_at": "Réinitialisé à",
|
||||||
|
"token_usage.title": "Utilisation des crédits IA",
|
||||||
|
"token_usage.tokens_remaining": "crédits restants",
|
||||||
|
"token_usage.tokens_used": "{{used}} / {{total}} crédits utilisés",
|
||||||
|
"token_usage_pill.billed": "Facturé",
|
||||||
|
"token_usage_pill.byok": "BYOK",
|
||||||
|
"token_usage_pill.credits": "Crédits",
|
||||||
|
"token_usage_pill.credits_usage": "Utilisation des crédits IA",
|
||||||
|
"token_usage_pill.duration": "Durée",
|
||||||
|
"token_usage_pill.model_info": "Infos modèle",
|
||||||
|
"token_usage_pill.multiplier": "Multiplicateur",
|
||||||
|
"token_usage_pill.provider_info": "Infos fournisseur",
|
||||||
|
"token_usage_pill.system": "Système",
|
||||||
|
"token_usage_pill.total": "Total",
|
||||||
|
"token_usage_pill.unknown": "Inconnu",
|
||||||
|
"usage_analysis.active_session": "Session active",
|
||||||
|
"usage_analysis.current_usage": "Utilisation actuelle",
|
||||||
|
"usage_analysis.detailed_description": "Aperçu complet de vos habitudes d'utilisation de l'IA et informations",
|
||||||
|
"usage_analysis.detailed_title": "Analyse d'utilisation de l'IA",
|
||||||
|
"usage_analysis.no_data": "Aucune donnée d'utilisation disponible",
|
||||||
|
"usage_analysis.resets_in": "Réinitialisé dans",
|
||||||
|
"usage_analysis.session_duration": "Durée : {{duration}}",
|
||||||
|
"usage_analysis.title": "Utilisation des crédits IA",
|
||||||
|
"usage_analysis.tokens_remaining": "crédits restants",
|
||||||
|
"usage_analysis.tokens_used": "Crédits utilisés",
|
||||||
|
"usage_analysis.total_credits": "Crédits totaux",
|
||||||
|
"usage_analysis.total_limit": "Limite totale",
|
||||||
|
"usage_analysis.view_details": "Voir les détails",
|
||||||
|
"usage_analysis.warning.general": "Envisagez de réduire l'utilisation pour éviter d'atteindre les limites",
|
||||||
|
"usage_analysis.warning.projected": "Limite projetée dans {{eta}}",
|
||||||
|
"usage_analysis.warning.rate": "Taux : {{rate}} tok/min",
|
||||||
|
"usage_analysis.warning.title": "Utilisation élevée de l'IA détectée",
|
||||||
|
"usage_analysis.window_remaining": "Crédits de fenêtre restants",
|
||||||
|
"welcome_description": "Je suis là pour vous aider avec vos besoins de lecture. Comment puis-je vous aider aujourd'hui ?",
|
||||||
|
"welcome_description_contextual": "Discutons de cette entrée ensemble"
|
||||||
|
}
|
||||||
|
|
@ -62,6 +62,8 @@
|
||||||
"features.title": "機能",
|
"features.title": "機能",
|
||||||
"integration.mcp.description": "安全なOAuth統合により、AI機能を拡張するMCP対応サービスに接続。",
|
"integration.mcp.description": "安全なOAuth統合により、AI機能を拡張するMCP対応サービスに接続。",
|
||||||
"integration.mcp.enabled": "MCPサービスを有効にする",
|
"integration.mcp.enabled": "MCPサービスを有効にする",
|
||||||
|
"integration.mcp.security.description": "OAuth認証トークンは、AES-GCM-256暗号化を使用して安全に保存され、資格情報を保護し、データプライバシーを確保します。",
|
||||||
|
"integration.mcp.security.title": "セキュリティ開示",
|
||||||
"integration.mcp.service.active": "アクティブ",
|
"integration.mcp.service.active": "アクティブ",
|
||||||
"integration.mcp.service.added": "MCPサービスが正常に追加されました",
|
"integration.mcp.service.added": "MCPサービスが正常に追加されました",
|
||||||
"integration.mcp.service.auth_message": "このサービスはOAuth認証が必要です。新しいウィンドウで認証を進めるには「認証を開く」をクリックしてください。",
|
"integration.mcp.service.auth_message": "このサービスはOAuth認証が必要です。新しいウィンドウで認証を進めるには「認証を開く」をクリックしてください。",
|
||||||
|
|
@ -75,11 +77,15 @@
|
||||||
"integration.mcp.service.connecting": "接続中...",
|
"integration.mcp.service.connecting": "接続中...",
|
||||||
"integration.mcp.service.connection_failed": "MCPサービスへの接続に失敗しました",
|
"integration.mcp.service.connection_failed": "MCPサービスへの接続に失敗しました",
|
||||||
"integration.mcp.service.degraded": "劣化",
|
"integration.mcp.service.degraded": "劣化",
|
||||||
|
"integration.mcp.service.delete_message": "このMCPサービスを削除してもよろしいですか?この操作は元に戻すことはできません。",
|
||||||
|
"integration.mcp.service.delete_title": "MCPサービスの削除",
|
||||||
"integration.mcp.service.deleted": "MCPサービスが正常に削除されました",
|
"integration.mcp.service.deleted": "MCPサービスが正常に削除されました",
|
||||||
|
"integration.mcp.service.disabled": "無効",
|
||||||
"integration.mcp.service.disconnect": "切断",
|
"integration.mcp.service.disconnect": "切断",
|
||||||
"integration.mcp.service.disconnected": "切断済み",
|
"integration.mcp.service.disconnected": "切断済み",
|
||||||
"integration.mcp.service.discover": "サービスを発見",
|
"integration.mcp.service.discover": "サービスを発見",
|
||||||
"integration.mcp.service.discovery_failed": "サービスエンドポイントの発見に失敗しました",
|
"integration.mcp.service.discovery_failed": "サービスエンドポイントの発見に失敗しました",
|
||||||
|
"integration.mcp.service.enabled": "有効",
|
||||||
"integration.mcp.service.endpoints": "OAuthエンドポイント",
|
"integration.mcp.service.endpoints": "OAuthエンドポイント",
|
||||||
"integration.mcp.service.error": "接続エラー",
|
"integration.mcp.service.error": "接続エラー",
|
||||||
"integration.mcp.service.healthy": "正常",
|
"integration.mcp.service.healthy": "正常",
|
||||||
|
|
@ -162,6 +168,7 @@
|
||||||
"mentions.section.entry": "エントリー",
|
"mentions.section.entry": "エントリー",
|
||||||
"mentions.section.feed": "フィード",
|
"mentions.section.feed": "フィード",
|
||||||
"mentions.section.view": "ビュー",
|
"mentions.section.view": "ビュー",
|
||||||
|
"new_shortcuts": "ショートカットを追加",
|
||||||
"personalize.description": "あなた自身について教えてください。そうすれば、パーソナライズされたAIの応答が得られます。",
|
"personalize.description": "あなた自身について教えてください。そうすれば、パーソナライズされたAIの応答が得られます。",
|
||||||
"personalize.prompt.help": "これにより、AIはあなたの好みに基づいてパーソナライズされた応答を提供できます。",
|
"personalize.prompt.help": "これにより、AIはあなたの好みに基づいてパーソナライズされた応答を提供できます。",
|
||||||
"personalize.prompt.placeholder": "あなた自身について、どのようにコンテンツを読みたいかを教えてください...",
|
"personalize.prompt.placeholder": "あなた自身について、どのようにコンテンツを読みたいかを教えてください...",
|
||||||
|
|
@ -171,6 +178,42 @@
|
||||||
"quick_actions.questions": "質問をする",
|
"quick_actions.questions": "質問をする",
|
||||||
"quick_actions.simplify": "簡単にする",
|
"quick_actions.simplify": "簡単にする",
|
||||||
"quick_actions.takeaways": "重要なポイント",
|
"quick_actions.takeaways": "重要なポイント",
|
||||||
|
"rate_limit.credits_left": "残り {{count}} クレジット",
|
||||||
|
"rate_limit.depleted": "AIクレジットが不足しています",
|
||||||
|
"rate_limit.minute": "分",
|
||||||
|
"rate_limit.minutes": "分",
|
||||||
|
"rate_limit.resets_at": "{{time}} にリセット",
|
||||||
|
"rate_limit.resets_in": "{{value}} 後にリセット",
|
||||||
|
"rate_limit.resets_next_month": "来月にリセット",
|
||||||
|
"rate_limit.resets_tomorrow": "明日にリセット",
|
||||||
|
"rate_limit.upgrade_plan_button": "無料トライアル",
|
||||||
|
"rate_limit.upgrade_to_get_more": "無料トライアルを開始して、より多くの AI クレジットを利用しましょう。",
|
||||||
|
"schedule.configuration_label": "設定",
|
||||||
|
"schedule.date_time_label": "日付と時刻",
|
||||||
|
"schedule.date_time_placeholder": "日付と時刻を選択",
|
||||||
|
"schedule.day_label": "曜日",
|
||||||
|
"schedule.day_placeholder": "曜日を選択",
|
||||||
|
"schedule.days.friday": "金曜日",
|
||||||
|
"schedule.days.monday": "月曜日",
|
||||||
|
"schedule.days.saturday": "土曜日",
|
||||||
|
"schedule.days.sunday": "日曜日",
|
||||||
|
"schedule.days.thursday": "木曜日",
|
||||||
|
"schedule.days.tuesday": "火曜日",
|
||||||
|
"schedule.days.wednesday": "水曜日",
|
||||||
|
"schedule.frequency.daily": "毎日",
|
||||||
|
"schedule.frequency.monthly": "毎月",
|
||||||
|
"schedule.frequency.once": "一度きり",
|
||||||
|
"schedule.frequency.weekly": "毎週",
|
||||||
|
"schedule.next_execution": "次: {{time}} ({{relative}})",
|
||||||
|
"schedule.no_upcoming": "今後の実行はありません",
|
||||||
|
"schedule.presets.daily_6pm": "毎日 18:00",
|
||||||
|
"schedule.presets.first_9am": "1日 9:00",
|
||||||
|
"schedule.presets.monday_9am": "月曜日 9:00",
|
||||||
|
"schedule.presets.tomorrow_9am": "明日 9:00",
|
||||||
|
"schedule.presets_title": "クイックプリセット",
|
||||||
|
"schedule.time_label": "時刻",
|
||||||
|
"schedule.time_placeholder": "時刻を選択",
|
||||||
|
"schedule.title": "スケジュール",
|
||||||
"session.interrupted.message": "前回のリクエストが最後まで完了しませんでした。もう一度送信してください。",
|
"session.interrupted.message": "前回のリクエストが最後まで完了しませんでした。もう一度送信してください。",
|
||||||
"session.interrupted.retry": "再試行",
|
"session.interrupted.retry": "再試行",
|
||||||
"settings.autoScrollWhenStreaming.description": "ストリーミング中にチャットの一番下まで自動スクロールします。",
|
"settings.autoScrollWhenStreaming.description": "ストリーミング中にチャットの一番下まで自動スクロールします。",
|
||||||
|
|
@ -180,6 +223,8 @@
|
||||||
"settings.panel_style.fixed": "固定",
|
"settings.panel_style.fixed": "固定",
|
||||||
"settings.panel_style.floating": "浮動",
|
"settings.panel_style.floating": "浮動",
|
||||||
"settings.panel_style.label": "パネルスタイル",
|
"settings.panel_style.label": "パネルスタイル",
|
||||||
|
"settings.showSplineButton.description": "右下隅にあるAIアシスタントインジケーターを表示または非表示にします。",
|
||||||
|
"settings.showSplineButton.label": "AIアシスタントインジケーター",
|
||||||
"settings.title": "AI 設定",
|
"settings.title": "AI 設定",
|
||||||
"shortcuts.actions.delete": "ショートカットを削除",
|
"shortcuts.actions.delete": "ショートカットを削除",
|
||||||
"shortcuts.actions.disable": "ショートカットを無効化",
|
"shortcuts.actions.disable": "ショートカットを無効化",
|
||||||
|
|
@ -278,11 +323,20 @@
|
||||||
"tasks.validation.title_required": "タイトルは必須です",
|
"tasks.validation.title_required": "タイトルは必須です",
|
||||||
"tasks.view_in_settings": "スケジュールされたタスクを表示",
|
"tasks.view_in_settings": "スケジュールされたタスクを表示",
|
||||||
"timeline.summary.title_template": "{{- datetime}} のタイムライン要約",
|
"timeline.summary.title_template": "{{- datetime}} のタイムライン要約",
|
||||||
|
"timeline_prompt.prompt.help": "AI がタイムラインを並び替える際に使われます。好みのテーマやソース、優先度を下げたい内容を書いてください。",
|
||||||
|
"timeline_prompt.prompt.placeholder": "AI タイムラインで優先表示したい内容や避けたい内容を記述してください...",
|
||||||
|
"timeline_prompt.saved": "タイムライン並び替えの設定を保存しました",
|
||||||
|
"timeline_prompt.title": "タイムライン並び替えプロンプト",
|
||||||
"timeline_summary.empty": "まもなくタイムラインの概要が表示されます。",
|
"timeline_summary.empty": "まもなくタイムラインの概要が表示されます。",
|
||||||
"timeline_summary.error": "現在タイムラインの概要を生成できません。",
|
"timeline_summary.error": "現在タイムラインの概要を生成できません。",
|
||||||
"timeline_summary.generating": "タイムラインを要約しています...",
|
"timeline_summary.generating": "タイムラインを要約しています...",
|
||||||
"timeline_summary.heading": "タイムラインに新しい内容はありますか",
|
"timeline_summary.heading": "タイムラインに新しい内容はありますか",
|
||||||
"timeline_summary.options.include": "タイムライン要約でチャット",
|
"timeline_summary.options.include": "タイムライン要約でチャット",
|
||||||
|
"token_usage.description": "AIトークンの消費と制限を監視します。",
|
||||||
|
"token_usage.resets_at": "リセット日時",
|
||||||
|
"token_usage.title": "トークン使用状況",
|
||||||
|
"token_usage.tokens_remaining": "残りトークン",
|
||||||
|
"token_usage.tokens_used": "{{used}} / {{total}} トークン使用済み",
|
||||||
"token_usage_pill.billed": "請求済み",
|
"token_usage_pill.billed": "請求済み",
|
||||||
"token_usage_pill.byok": "BYOK",
|
"token_usage_pill.byok": "BYOK",
|
||||||
"token_usage_pill.credits": "クレジット",
|
"token_usage_pill.credits": "クレジット",
|
||||||
|
|
|
||||||
|
|
@ -77,11 +77,15 @@
|
||||||
"integration.mcp.service.connecting": "连接中...",
|
"integration.mcp.service.connecting": "连接中...",
|
||||||
"integration.mcp.service.connection_failed": "连接 MCP 服务失败",
|
"integration.mcp.service.connection_failed": "连接 MCP 服务失败",
|
||||||
"integration.mcp.service.degraded": "降级",
|
"integration.mcp.service.degraded": "降级",
|
||||||
|
"integration.mcp.service.delete_message": "确定要删除此 MCP 服务吗?此操作无法撤销。",
|
||||||
|
"integration.mcp.service.delete_title": "删除 MCP 服务",
|
||||||
"integration.mcp.service.deleted": "MCP 服务删除成功",
|
"integration.mcp.service.deleted": "MCP 服务删除成功",
|
||||||
|
"integration.mcp.service.disabled": "已禁用",
|
||||||
"integration.mcp.service.disconnect": "断开连接",
|
"integration.mcp.service.disconnect": "断开连接",
|
||||||
"integration.mcp.service.disconnected": "已断开连接",
|
"integration.mcp.service.disconnected": "已断开连接",
|
||||||
"integration.mcp.service.discover": "发现服务",
|
"integration.mcp.service.discover": "发现服务",
|
||||||
"integration.mcp.service.discovery_failed": "发现服务端点失败",
|
"integration.mcp.service.discovery_failed": "发现服务端点失败",
|
||||||
|
"integration.mcp.service.enabled": "已启用",
|
||||||
"integration.mcp.service.endpoints": "OAuth 端点",
|
"integration.mcp.service.endpoints": "OAuth 端点",
|
||||||
"integration.mcp.service.error": "连接错误",
|
"integration.mcp.service.error": "连接错误",
|
||||||
"integration.mcp.service.healthy": "健康",
|
"integration.mcp.service.healthy": "健康",
|
||||||
|
|
@ -164,6 +168,7 @@
|
||||||
"mentions.section.entry": "条目",
|
"mentions.section.entry": "条目",
|
||||||
"mentions.section.feed": "订阅源",
|
"mentions.section.feed": "订阅源",
|
||||||
"mentions.section.view": "视图",
|
"mentions.section.view": "视图",
|
||||||
|
"new_shortcuts": "新增快捷方式",
|
||||||
"personalize.description": "介绍一下您自己,以获得个性化的 AI 响应。",
|
"personalize.description": "介绍一下您自己,以获得个性化的 AI 响应。",
|
||||||
"personalize.prompt.help": "这有助于 AI 根据您的偏好提供个性化响应。",
|
"personalize.prompt.help": "这有助于 AI 根据您的偏好提供个性化响应。",
|
||||||
"personalize.prompt.placeholder": "介绍一下您自己以及您希望如何阅读内容...",
|
"personalize.prompt.placeholder": "介绍一下您自己以及您希望如何阅读内容...",
|
||||||
|
|
@ -218,6 +223,8 @@
|
||||||
"settings.panel_style.fixed": "固定",
|
"settings.panel_style.fixed": "固定",
|
||||||
"settings.panel_style.floating": "浮动",
|
"settings.panel_style.floating": "浮动",
|
||||||
"settings.panel_style.label": "面板样式",
|
"settings.panel_style.label": "面板样式",
|
||||||
|
"settings.showSplineButton.description": "显示或隐藏右下角的 AI 助手指示器。",
|
||||||
|
"settings.showSplineButton.label": "AI 助手指示器",
|
||||||
"settings.title": "AI 设置",
|
"settings.title": "AI 设置",
|
||||||
"shortcuts.actions.delete": "删除快捷方式",
|
"shortcuts.actions.delete": "删除快捷方式",
|
||||||
"shortcuts.actions.disable": "禁用快捷方式",
|
"shortcuts.actions.disable": "禁用快捷方式",
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@
|
||||||
"analytics.no_data": "所選期間沒有可用的使用資料",
|
"analytics.no_data": "所選期間沒有可用的使用資料",
|
||||||
"analytics.no_history": "沒有可用的使用歷史",
|
"analytics.no_history": "沒有可用的使用歷史",
|
||||||
"analytics.operation_types.chat": "聊天",
|
"analytics.operation_types.chat": "聊天",
|
||||||
|
"analytics.operation_types.onboarding": "入門",
|
||||||
|
"analytics.operation_types.task": "任務",
|
||||||
"analytics.operation_types.title_generation": "標題生成",
|
"analytics.operation_types.title_generation": "標題生成",
|
||||||
"analytics.patterns_placeholder": "模式分析將顯示高峰使用時間和習慣",
|
"analytics.patterns_placeholder": "模式分析將顯示高峰使用時間和習慣",
|
||||||
"analytics.tabs.efficiency": "效率",
|
"analytics.tabs.efficiency": "效率",
|
||||||
|
|
@ -107,6 +109,20 @@
|
||||||
"integration.security.description": "OAuth 驗證令牌和 AI BYOK API 金鑰使用 AES-GCM-256 加密技術安全儲存,以保護您的憑據並確保資料隱私。",
|
"integration.security.description": "OAuth 驗證令牌和 AI BYOK API 金鑰使用 AES-GCM-256 加密技術安全儲存,以保護您的憑據並確保資料隱私。",
|
||||||
"integration.security.title": "安全聲明",
|
"integration.security.title": "安全聲明",
|
||||||
"integration.title": "整合",
|
"integration.title": "整合",
|
||||||
|
"memories.actions.load_more": "載入更多",
|
||||||
|
"memories.actions.loading": "載入中...",
|
||||||
|
"memories.importance": "重要性",
|
||||||
|
"memories.list.description": "AI 會在與你的交流中逐漸了解你。這些記憶能幫助它更好地理解你的偏好與習慣。",
|
||||||
|
"memories.list.empty.description": "隨著你與 AI 的對話,它會逐漸學習你的偏好和習慣。記憶會顯示在這裡。",
|
||||||
|
"memories.list.empty.title": "還沒有印象",
|
||||||
|
"memories.section.title": "AI 對你的印象",
|
||||||
|
"memories.toast.deleted": "記憶已刪除",
|
||||||
|
"memories.toast.failed": "儲存記憶失敗",
|
||||||
|
"memories.toast.processing": "處理中...",
|
||||||
|
"memories.toast.saved": "記憶已儲存",
|
||||||
|
"memories.toast.saving": "正在儲存記憶...",
|
||||||
|
"memories.toast.updated": "記憶已更新",
|
||||||
|
"memories.toast.updating": "正在更新記憶...",
|
||||||
"mentions.date.relative.last_15_days.label": "最近 15 天",
|
"mentions.date.relative.last_15_days.label": "最近 15 天",
|
||||||
"mentions.date.relative.last_15_days.search": "最近 15 天|近半個月",
|
"mentions.date.relative.last_15_days.search": "最近 15 天|近半個月",
|
||||||
"mentions.date.relative.last_30_days.label": "最近 30 天",
|
"mentions.date.relative.last_30_days.label": "最近 30 天",
|
||||||
|
|
@ -162,6 +178,16 @@
|
||||||
"quick_actions.questions": "提出問題",
|
"quick_actions.questions": "提出問題",
|
||||||
"quick_actions.simplify": "簡化內容",
|
"quick_actions.simplify": "簡化內容",
|
||||||
"quick_actions.takeaways": "關鍵重點",
|
"quick_actions.takeaways": "關鍵重點",
|
||||||
|
"rate_limit.credits_left": "剩餘 {{count}} 額度",
|
||||||
|
"rate_limit.depleted": "AI 額度已用盡",
|
||||||
|
"rate_limit.minute": "分鐘",
|
||||||
|
"rate_limit.minutes": "分鐘",
|
||||||
|
"rate_limit.resets_at": "將於 {{time}} 重設",
|
||||||
|
"rate_limit.resets_in": "將在 {{value}} 後重設",
|
||||||
|
"rate_limit.resets_next_month": "將於下個月重設",
|
||||||
|
"rate_limit.resets_tomorrow": "將於明天重設",
|
||||||
|
"rate_limit.upgrade_plan_button": "免費試用",
|
||||||
|
"rate_limit.upgrade_to_get_more": "開啟免費試用以獲得更多 AI 額度。",
|
||||||
"schedule.configuration_label": "設定",
|
"schedule.configuration_label": "設定",
|
||||||
"schedule.date_time_label": "日期和時間",
|
"schedule.date_time_label": "日期和時間",
|
||||||
"schedule.date_time_placeholder": "選擇日期和時間",
|
"schedule.date_time_placeholder": "選擇日期和時間",
|
||||||
|
|
@ -188,6 +214,8 @@
|
||||||
"schedule.time_label": "時間",
|
"schedule.time_label": "時間",
|
||||||
"schedule.time_placeholder": "選擇時間",
|
"schedule.time_placeholder": "選擇時間",
|
||||||
"schedule.title": "排程",
|
"schedule.title": "排程",
|
||||||
|
"session.interrupted.message": "上次請求似乎未完成,請重試以繼續。",
|
||||||
|
"session.interrupted.retry": "重新傳送",
|
||||||
"settings.autoScrollWhenStreaming.description": "在串流時自動捲動到聊天底部。",
|
"settings.autoScrollWhenStreaming.description": "在串流時自動捲動到聊天底部。",
|
||||||
"settings.autoScrollWhenStreaming.label": "串流時自動捲動",
|
"settings.autoScrollWhenStreaming.label": "串流時自動捲動",
|
||||||
"settings.description": "設定您的 AI 體驗並建立自訂快速鍵",
|
"settings.description": "設定您的 AI 體驗並建立自訂快速鍵",
|
||||||
|
|
@ -198,6 +226,9 @@
|
||||||
"settings.showSplineButton.description": "顯示或隱藏右下角的 AI 助手指示器。",
|
"settings.showSplineButton.description": "顯示或隱藏右下角的 AI 助手指示器。",
|
||||||
"settings.showSplineButton.label": "AI 助手指示器",
|
"settings.showSplineButton.label": "AI 助手指示器",
|
||||||
"settings.title": "AI 設定",
|
"settings.title": "AI 設定",
|
||||||
|
"shortcuts.actions.delete": "刪除快速鍵",
|
||||||
|
"shortcuts.actions.disable": "停用快速鍵",
|
||||||
|
"shortcuts.actions.edit": "編輯快速鍵",
|
||||||
"shortcuts.add": "新增快速鍵",
|
"shortcuts.add": "新增快速鍵",
|
||||||
"shortcuts.added": "快捷方式新增成功",
|
"shortcuts.added": "快捷方式新增成功",
|
||||||
"shortcuts.context_menu.empty.entry": "尚無可用於條目詳情的快速鍵",
|
"shortcuts.context_menu.empty.entry": "尚無可用於條目詳情的快速鍵",
|
||||||
|
|
@ -292,6 +323,10 @@
|
||||||
"tasks.validation.title_required": "標題為必填項目",
|
"tasks.validation.title_required": "標題為必填項目",
|
||||||
"tasks.view_in_settings": "在設定中檢視已排程任務",
|
"tasks.view_in_settings": "在設定中檢視已排程任務",
|
||||||
"timeline.summary.title_template": "{{- datetime}} 時間軸摘要",
|
"timeline.summary.title_template": "{{- datetime}} 時間軸摘要",
|
||||||
|
"timeline_prompt.prompt.help": "這些資訊會用於 AI 時間軸排序,請寫下你偏好的主題、來源,以及想降低權重的內容。",
|
||||||
|
"timeline_prompt.prompt.placeholder": "描述你希望 AI 時間軸優先顯示或避免的內容……",
|
||||||
|
"timeline_prompt.saved": "時間軸排序偏好已儲存",
|
||||||
|
"timeline_prompt.title": "AI 時間軸排序提示",
|
||||||
"timeline_summary.empty": "時間軸摘要即將呈現。",
|
"timeline_summary.empty": "時間軸摘要即將呈現。",
|
||||||
"timeline_summary.error": "暫時無法產生時間軸摘要。",
|
"timeline_summary.error": "暫時無法產生時間軸摘要。",
|
||||||
"timeline_summary.generating": "正在產生時間軸摘要...",
|
"timeline_summary.generating": "正在產生時間軸摘要...",
|
||||||
|
|
@ -323,12 +358,14 @@
|
||||||
"usage_analysis.title": "令牌使用狀況",
|
"usage_analysis.title": "令牌使用狀況",
|
||||||
"usage_analysis.tokens_remaining": "剩餘令牌",
|
"usage_analysis.tokens_remaining": "剩餘令牌",
|
||||||
"usage_analysis.tokens_used": "已使用令牌",
|
"usage_analysis.tokens_used": "已使用令牌",
|
||||||
|
"usage_analysis.total_credits": "總額度",
|
||||||
"usage_analysis.total_limit": "總限制",
|
"usage_analysis.total_limit": "總限制",
|
||||||
"usage_analysis.view_details": "檢視詳情",
|
"usage_analysis.view_details": "檢視詳情",
|
||||||
"usage_analysis.warning.general": "請考慮降低使用以避免達到限制",
|
"usage_analysis.warning.general": "請考慮降低使用以避免達到限制",
|
||||||
"usage_analysis.warning.projected": "預計在 {{eta}} 達到限制",
|
"usage_analysis.warning.projected": "預計在 {{eta}} 達到限制",
|
||||||
"usage_analysis.warning.rate": "速率:{{rate}} 令牌/分鐘",
|
"usage_analysis.warning.rate": "速率:{{rate}} 令牌/分鐘",
|
||||||
"usage_analysis.warning.title": "檢測到較高的 AI 使用量",
|
"usage_analysis.warning.title": "檢測到較高的 AI 使用量",
|
||||||
|
"usage_analysis.window_remaining": "視窗內剩餘額度",
|
||||||
"welcome_description": "我在這裡幫助您處理閱讀需求。今天我能為您做些什麼?",
|
"welcome_description": "我在這裡幫助您處理閱讀需求。今天我能為您做些什麼?",
|
||||||
"welcome_description_contextual": "讓我們一起討論這個條目"
|
"welcome_description_contextual": "讓我們一起討論這個條目"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,545 @@
|
||||||
|
{
|
||||||
|
"achievement.all_done": "Tout est fait !",
|
||||||
|
"achievement.alpha_tester": "Testeur Alpha",
|
||||||
|
"achievement.alpha_tester_description": "Vous êtes un testeur alpha de Folo",
|
||||||
|
"achievement.description": "Soyez un joueur hardcore et mintez des NFT.",
|
||||||
|
"achievement.first_claim_feed": "Propriétaire de Flux",
|
||||||
|
"achievement.first_claim_feed_description": "Vous possédez votre flux sur Folo",
|
||||||
|
"achievement.first_create_list": "Créateur de Liste",
|
||||||
|
"achievement.first_create_list_description": "Vous avez créé votre première liste sur Folo",
|
||||||
|
"achievement.follow_special_feed": "Abonné Flux Spécial",
|
||||||
|
"achievement.follow_special_feed_description": "Vous avez suivi le flux spécial sur Folo",
|
||||||
|
"achievement.list_subscribe_100": "100 Abonnés Liste",
|
||||||
|
"achievement.list_subscribe_100_description": "100 abonnés se sont abonnés à la liste que vous avez créée",
|
||||||
|
"achievement.list_subscribe_50": "50 Abonnés Liste",
|
||||||
|
"achievement.list_subscribe_500": "500 Abonnés Liste",
|
||||||
|
"achievement.list_subscribe_500_description": "500 abonnés se sont abonnés à la liste que vous avez créée",
|
||||||
|
"achievement.list_subscribe_50_description": "50 abonnés se sont abonnés à la liste que vous avez créée",
|
||||||
|
"achievement.nft_coming_soon": "Vous ne pouvez pas minter de NFT pour le moment. Une fois prêts, ils seront automatiquement crédités sur votre compte.",
|
||||||
|
"achievement.product_hunt_vote": "Product Hunt Upvoter",
|
||||||
|
"achievement.product_hunt_vote_description": "Vous avez soutenu Folo sur Product Hunt",
|
||||||
|
"activation.activate": "Activer",
|
||||||
|
"activation.description": "Pendant la phase de test public, vous avez besoin d'un code d'invitation pour utiliser cette fonctionnalité.",
|
||||||
|
"activation.plan.description": "Vous utilisez actuellement le plan gratuit. Démarrez l'essai gratuit pour débloquer plus de fonctionnalités.",
|
||||||
|
"activation.plan.title": "Mettre à niveau le plan (Essai gratuit)",
|
||||||
|
"activation.plan.upgrade": "Essai gratuit",
|
||||||
|
"activation.title": "Code d'invitation",
|
||||||
|
"ai.summary_not_available": "Résumé non disponible",
|
||||||
|
"ai.summary_upgrade_required_description": "Débloquez les résumés IA illimités, les traductions et plus de fonctionnalités intelligentes avec le plan Pro.",
|
||||||
|
"ai.summary_upgrade_required_title": "Démarrez l'essai gratuit pour continuer à utiliser le résumé IA",
|
||||||
|
"ai.summary_upgrade_view_plans": "Voir les plans",
|
||||||
|
"app.copy_logo_svg": "Copier le logo SVG",
|
||||||
|
"app.copy_logo_text_svg": "Copier le texte du logo SVG",
|
||||||
|
"app.toggle_sidebar": "Basculer la barre latérale",
|
||||||
|
"discover.any_url_or_keyword": "N'importe quelle URL ou mot-clé",
|
||||||
|
"discover.default_option": " (défaut)",
|
||||||
|
"discover.feed_description": "La description de ce flux est la suivante, et vous pouvez remplir le formulaire de paramètres avec les informations pertinentes.",
|
||||||
|
"discover.feed_maintainers": "Ce flux est fourni par RSSHub, crédit à <maintainers />",
|
||||||
|
"discover.import.click_to_upload": "Cliquez pour télécharger le fichier OPML",
|
||||||
|
"discover.import.conflictItems": "Éléments en conflit",
|
||||||
|
"discover.import.import_completed_with_issues": "Importation terminée avec quelques problèmes",
|
||||||
|
"discover.import.import_successful": "Importation terminée avec succès",
|
||||||
|
"discover.import.noItems": "Aucun élément",
|
||||||
|
"discover.import.no_feeds_found": "Aucun flux trouvé correspondant à votre recherche.",
|
||||||
|
"discover.import.opml": "Fichier OPML",
|
||||||
|
"discover.import.opml_step1": "Exporter l'OPML depuis votre lecteur RSS",
|
||||||
|
"discover.import.opml_step1_feedly": "Exporter l'OPML depuis Feedly",
|
||||||
|
"discover.import.opml_step1_feedly_step1": "Ouvrir <Link />.",
|
||||||
|
"discover.import.opml_step1_feedly_step2": "Cliquez sur le bouton \"Download Your Feedly OPML\".",
|
||||||
|
"discover.import.opml_step1_inoreader": "Exporter l'OPML depuis Inoreader",
|
||||||
|
"discover.import.opml_step1_inoreader_step1": "Ouvrir <Link />.",
|
||||||
|
"discover.import.opml_step1_inoreader_step2": "Passez à l'onglet \"SYSTEM FOLDERS\".",
|
||||||
|
"discover.import.opml_step1_inoreader_step3": "Cliquez sur le bouton \"OPML\" sur le côté droit de \"Newsfeed\".",
|
||||||
|
"discover.import.opml_step1_other": "Exporter l'OPML depuis d'autres lecteurs",
|
||||||
|
"discover.import.opml_step1_other_step1": "OPML est un format ouvert largement pris en charge et est essentiellement la norme pour partager des listes d'abonnement aux flux. Presque tous les lecteurs RSS permettent l'importation et l'exportation OPML. Si vous ne savez pas comment faire, veuillez consulter le manuel d'aide de votre lecteur RSS ou rejoindre notre communauté pour obtenir de l'aide.",
|
||||||
|
"discover.import.opml_step2": "Importer l'OPML vers Folo",
|
||||||
|
"discover.import.parse_opml": "Analyser l'OPML",
|
||||||
|
"discover.import.parsedErrorItems": "Éléments en erreur d'analyse",
|
||||||
|
"discover.import.preview_opml_content": "Aperçu du contenu OPML",
|
||||||
|
"discover.import.quota_exceeded": "Quota dépassé",
|
||||||
|
"discover.import.quota_exceeded_warning": "Vous avez sélectionné plus de flux que votre quota restant ne le permet. Veuillez désélectionner certains flux pour continuer.",
|
||||||
|
"discover.import.quota_limit_reached": "Limite de quota atteinte",
|
||||||
|
"discover.import.quota_status": "Quota d'importation :",
|
||||||
|
"discover.import.quota_warning": "Il vous reste {{remaining}} flux dans votre quota.",
|
||||||
|
"discover.import.remaining_quota": "Vous pouvez importer {{remaining}} flux supplémentaires",
|
||||||
|
"discover.import.result": "<SuccessfulNum /> flux ont été importés avec succès.<br /><ConflictNum /> étaient déjà abonnés.<br /><ErrorNum /> ont échoué à l'importation.",
|
||||||
|
"discover.import.search_feeds_placeholder": "Rechercher des flux...",
|
||||||
|
"discover.import.select_all_feeds": "Sélectionner tous les flux",
|
||||||
|
"discover.import.select_all_filtered": "Sélectionner tous les filtrés",
|
||||||
|
"discover.import.select_feeds_description": "Vérifiez et sélectionnez les flux que vous souhaitez importer. Tous les flux sont sélectionnés par défaut.",
|
||||||
|
"discover.import.select_feeds_to_import": "Sélectionner les flux à importer",
|
||||||
|
"discover.import.successfulItems": "Éléments réussis",
|
||||||
|
"discover.inbox.actions": "Actions",
|
||||||
|
"discover.inbox.description": "Vous pouvez recevoir des informations par email et webhooks via la boîte de réception.",
|
||||||
|
"discover.inbox.email": "Email",
|
||||||
|
"discover.inbox.handle": "Identifiant",
|
||||||
|
"discover.inbox.no_inbox": "Vous n'avez actuellement aucune boîte de réception, cliquez sur le bouton ci-dessous pour créer votre première boîte de réception.",
|
||||||
|
"discover.inbox.secret": "Secret",
|
||||||
|
"discover.inbox.title": "Titre",
|
||||||
|
"discover.inbox.webhooks_docs": "Docs Webhooks",
|
||||||
|
"discover.inbox_create": "Nouvelle boîte de réception",
|
||||||
|
"discover.inbox_create_description": "Vous n'avez pas encore de boîte de réception. Créez une boîte de réception pour recevoir des informations.",
|
||||||
|
"discover.inbox_create_error": "Échec de la création de la boîte de réception.",
|
||||||
|
"discover.inbox_create_success": "Boîte de réception créée avec succès.",
|
||||||
|
"discover.inbox_destroy": "Détruire",
|
||||||
|
"discover.inbox_destroy_confirm": "Confirmer la destruction de la boîte de réception ?",
|
||||||
|
"discover.inbox_destroy_error": "Échec de la destruction de la boîte de réception.",
|
||||||
|
"discover.inbox_destroy_success": "Boîte de réception détruite avec succès.",
|
||||||
|
"discover.inbox_destroy_warning": "Attention : une fois détruite, l'email ne sera plus disponible, et tout le contenu sera définitivement supprimé et irrécupérable.",
|
||||||
|
"discover.inbox_handle": "Identifiant",
|
||||||
|
"discover.inbox_title": "Titre",
|
||||||
|
"discover.inbox_update": "Mettre à jour",
|
||||||
|
"discover.inbox_update_error": "Échec de la modification de la boîte de réception.",
|
||||||
|
"discover.inbox_update_success": "Boîte de réception mise à jour avec succès.",
|
||||||
|
"discover.popular": "Populaire",
|
||||||
|
"discover.preview": "Aperçu",
|
||||||
|
"discover.rss_hub_route": "Route RSSHub",
|
||||||
|
"discover.rss_url": "URL RSS",
|
||||||
|
"discover.search.results_one": "Trouvé {{count}} flux",
|
||||||
|
"discover.search.results_other": "Trouvé {{count}} flux",
|
||||||
|
"discover.search.results_zero": "Aucun flux trouvé",
|
||||||
|
"discover.select_placeholder": "Sélectionner",
|
||||||
|
"discover.target.feeds": "Flux",
|
||||||
|
"discover.target.label": "Rechercher",
|
||||||
|
"discover.target.lists": "Listes",
|
||||||
|
"discover.tips.auto_detect": "Détection auto du type d'entrée",
|
||||||
|
"discover.tips.search_keyword": "Recherche par mot-clé, URL, flux RSS ou route RSSHub",
|
||||||
|
"discover.tools.description": "Plus d'outils",
|
||||||
|
"discover.tools.import": "Importer OPML",
|
||||||
|
"discover.tools.inbox": "Boîte de réception",
|
||||||
|
"discover.tools.transform": "HTML vers RSS",
|
||||||
|
"discover.tools.user": "Suivre l'utilisateur",
|
||||||
|
"entry.click_to_return": "Faites défiler vers le haut ou cliquez ici pour revenir",
|
||||||
|
"entry.exit_detail": "Revenir à la chronologie",
|
||||||
|
"entry.scroll_up_to_exit": "Faites défiler vers le haut pour revenir à la chronologie",
|
||||||
|
"entry_actions.copied_notify": "{{which}} copié dans le presse-papier.",
|
||||||
|
"entry_actions.copy_link": "Copier le lien",
|
||||||
|
"entry_actions.copy_title": "Copier le titre",
|
||||||
|
"entry_actions.delete": "Supprimer",
|
||||||
|
"entry_actions.deleted": "Supprimé.",
|
||||||
|
"entry_actions.export_as_pdf": "Exporter en PDF",
|
||||||
|
"entry_actions.failed_to_delete": "Échec de la suppression.",
|
||||||
|
"entry_actions.failed_to_login_to_qbittorrent": "Échec de la connexion à qBittorrent",
|
||||||
|
"entry_actions.failed_to_save_to_cubox": "Échec de l'enregistrement dans Cubox.",
|
||||||
|
"entry_actions.failed_to_save_to_eagle": "Échec de l'enregistrement dans Eagle.",
|
||||||
|
"entry_actions.failed_to_save_to_instapaper": "Échec de l'enregistrement dans Instapaper.",
|
||||||
|
"entry_actions.failed_to_save_to_obsidian": "Échec de l'enregistrement dans Obsidian",
|
||||||
|
"entry_actions.failed_to_save_to_outline": "Échec de l'enregistrement dans Outline.",
|
||||||
|
"entry_actions.failed_to_save_to_qbittorrent": "Échec du téléchargement avec qBittorrent",
|
||||||
|
"entry_actions.failed_to_save_to_readeck": "Échec de l'enregistrement dans Readeck.",
|
||||||
|
"entry_actions.failed_to_save_to_readwise": "Échec de l'enregistrement dans Readwise.",
|
||||||
|
"entry_actions.failed_to_save_to_zotero": "Échec de l'enregistrement dans Zotero.",
|
||||||
|
"entry_actions.image_gallery": "Galerie d'images",
|
||||||
|
"entry_actions.image_gallery_description": "Lorsqu'il y a plusieurs grandes images dans l'article, vous pouvez parcourir toutes les images de l'article dans la modale Galerie",
|
||||||
|
"entry_actions.mark_above_as_read": "Marquer ci-dessus comme lu",
|
||||||
|
"entry_actions.mark_as_read": "Marquer comme Lu / Non lu",
|
||||||
|
"entry_actions.mark_as_unread": "Marquer comme Non lu",
|
||||||
|
"entry_actions.mark_below_as_read": "Marquer ci-dessous comme lu",
|
||||||
|
"entry_actions.no_bittorrent_urls_found": "Aucune URL BitTorrent trouvée dans cette entrée.",
|
||||||
|
"entry_actions.open_in_browser": "Ouvrir dans {{which}}",
|
||||||
|
"entry_actions.recent_reader": "Lecteur récent :",
|
||||||
|
"entry_actions.save_media_to_eagle": "Enregistrer le média dans Eagle",
|
||||||
|
"entry_actions.save_to_cubox": "Enregistrer dans Cubox",
|
||||||
|
"entry_actions.save_to_instapaper": "Enregistrer dans Instapaper",
|
||||||
|
"entry_actions.save_to_obsidian": "Enregistrer dans Obsidian",
|
||||||
|
"entry_actions.save_to_outline": "Enregistrer dans Outline",
|
||||||
|
"entry_actions.save_to_qbittorrent": "Télécharger avec qBittorrent",
|
||||||
|
"entry_actions.save_to_readeck": "Enregistrer dans Readeck",
|
||||||
|
"entry_actions.save_to_readwise": "Enregistrer dans Readwise",
|
||||||
|
"entry_actions.save_to_zotero": "Enregistrer dans Zotero",
|
||||||
|
"entry_actions.saved_to_cubox": "Enregistré dans Cubox",
|
||||||
|
"entry_actions.saved_to_eagle": "Enregistré dans Eagle.",
|
||||||
|
"entry_actions.saved_to_instapaper": "Enregistré dans Instapaper.",
|
||||||
|
"entry_actions.saved_to_obsidian": "Enregistré dans Obsidian",
|
||||||
|
"entry_actions.saved_to_outline": "Enregistré dans Outline.",
|
||||||
|
"entry_actions.saved_to_qbittorrent": "Torrents ajoutés à qBittorrent.",
|
||||||
|
"entry_actions.saved_to_readeck": "Enregistré dans Readeck.",
|
||||||
|
"entry_actions.saved_to_readwise": "Enregistré dans Readwise.",
|
||||||
|
"entry_actions.saved_to_zotero": "Enregistré dans Zotero",
|
||||||
|
"entry_actions.share": "Partager",
|
||||||
|
"entry_actions.star": "Favori / Non favori",
|
||||||
|
"entry_actions.starred": "Favori.",
|
||||||
|
"entry_actions.toggle_ai_summary": "Résumé IA",
|
||||||
|
"entry_actions.toggle_ai_translation": "Traduction IA",
|
||||||
|
"entry_actions.unstar": "Retirer des favoris",
|
||||||
|
"entry_actions.unstarred": "Retiré des favoris.",
|
||||||
|
"entry_actions.view_source_content": "Voir le contenu source",
|
||||||
|
"entry_actions.view_source_content_description": "Essayez de lire le contenu de cet article dans le style du site source",
|
||||||
|
"entry_actions.warn_info_ai_chat_pinned_tip": "Cette fonctionnalité est actuellement en test gris, et seul un petit nombre d'utilisateurs a les permissions d'accès.",
|
||||||
|
"entry_actions.warn_info_for_desktop": "Cette fonctionnalité est disponible uniquement dans l'application de bureau",
|
||||||
|
"entry_column.filtered_content_tip": "Vous avez du contenu filtré masqué.",
|
||||||
|
"entry_column.filtered_content_tip_2": "En plus des entrées affichées ci-dessus, il y a aussi du contenu filtré.",
|
||||||
|
"entry_column.refreshing": "Actualisation des nouvelles entrées...",
|
||||||
|
"entry_content.ai_summary": "Résumé IA",
|
||||||
|
"entry_content.fetching_content": "Récupération du contenu original et traitement...",
|
||||||
|
"entry_content.fetching_content_failed": "Lisibilité : Échec de la récupération du contenu original, réessayez plus tard ou revenez au contenu RSS.",
|
||||||
|
"entry_content.header.play_tts": "Lire TTS",
|
||||||
|
"entry_content.header.play_tts_description": "Sélectionnez une voix TTS dans les paramètres et démarrez TTS pour convertir en contenu audio",
|
||||||
|
"entry_content.header.readability": "Lisibilité",
|
||||||
|
"entry_content.header.readability_description": "Utiliser la capacité de lisibilité pour obtenir le contenu original du site web et l'analyser peut permettre un contenu plus lisible. Recommandé lorsque le contenu RSS n'est pas disponible ou incomplet.",
|
||||||
|
"entry_content.no_content": "Aucun média disponible",
|
||||||
|
"entry_content.readability_notice": "Ce contenu est fourni par Readability. Si vous trouvez des anomalies typographiques, veuillez vous rendre sur le site source pour voir le contenu original.",
|
||||||
|
"entry_content.render_error": "Erreur de rendu :",
|
||||||
|
"entry_content.report_issue": "Signaler un problème",
|
||||||
|
"entry_content.selection_toolbar.ask_ai": "Demander à l'IA",
|
||||||
|
"entry_content.selection_toolbar.copied": "Copié",
|
||||||
|
"entry_content.selection_toolbar.copy": "Copier",
|
||||||
|
"entry_content.selection_toolbar.copy_image": "Copier l'image",
|
||||||
|
"entry_content.selection_toolbar.copying": "Copie...",
|
||||||
|
"entry_content.selection_toolbar.generating": "Génération...",
|
||||||
|
"entry_content.selection_toolbar.poster_copied": "Affiche copiée dans le presse-papier",
|
||||||
|
"entry_content.selection_toolbar.poster_copy_failed": "Échec de la copie de l'affiche",
|
||||||
|
"entry_content.selection_toolbar.share": "Partager",
|
||||||
|
"entry_content.selection_toolbar.share_poster": "Partager l'affiche",
|
||||||
|
"entry_content.web_app_notice": "L'application web ne prend peut-être pas en charge ce type de contenu. Mais vous pouvez télécharger l'application de bureau.",
|
||||||
|
"entry_list.zero_unread": "Zéro non lu",
|
||||||
|
"entry_list_header.ai_timeline": "Chronologie IA",
|
||||||
|
"entry_list_header.ai_timeline_loading": "Réorganisation de la chronologie avec l'IA...",
|
||||||
|
"entry_list_header.ai_timeline_prompt_required": "Définissez d'abord votre invite de tri de chronologie IA",
|
||||||
|
"entry_list_header.grid": "Grille",
|
||||||
|
"entry_list_header.image_only": "Image uniquement",
|
||||||
|
"entry_list_header.items": "éléments",
|
||||||
|
"entry_list_header.masonry": "Maçonnerie",
|
||||||
|
"entry_list_header.masonry_column": "Colonne Maçonnerie",
|
||||||
|
"entry_list_header.preview_mode": "Mode Aperçu",
|
||||||
|
"entry_list_header.refetch": "Rafraîchir",
|
||||||
|
"entry_list_header.refresh": "Actualiser",
|
||||||
|
"entry_list_header.show_all": "Tout afficher",
|
||||||
|
"entry_list_header.show_unread_only": "Afficher non lus uniquement",
|
||||||
|
"entry_list_header.timeline_summary": "Résumer la chronologie",
|
||||||
|
"entry_list_header.unread": "non lu",
|
||||||
|
"feed.actions.follow": "Suivre",
|
||||||
|
"feed.actions.followed": "Suivi",
|
||||||
|
"feed.followsAndFeeds": "{{subscriptionCount}} {{subscriptionNoun}} et {{feedsCount}} {{feedsNoun}} sur {{appName}}",
|
||||||
|
"feed.read_one": "lu",
|
||||||
|
"feed.read_other": "lus",
|
||||||
|
"feed_category.onboarding_feed": "Cette catégorie contient des flux d'intégration",
|
||||||
|
"feed_claim_modal.choose_verification_method": "Il y a trois façons de choisir, vous pouvez en choisir une pour vérifier.",
|
||||||
|
"feed_claim_modal.claim_button": "Réclamer",
|
||||||
|
"feed_claim_modal.content_instructions": "Copiez le contenu ci-dessous et publiez-le sur votre dernier flux RSS.",
|
||||||
|
"feed_claim_modal.description_current": "Description actuelle :",
|
||||||
|
"feed_claim_modal.description_instructions": "Copiez le contenu suivant et collez-le dans le champ <code /> de votre flux RSS.",
|
||||||
|
"feed_claim_modal.failed_to_load": "Échec du chargement du message de réclamation",
|
||||||
|
"feed_claim_modal.rss_format_choice": "Les générateurs RSS ont généralement deux formats au choix. Veuillez copier les formats XML et JSON ci-dessous selon vos besoins.",
|
||||||
|
"feed_claim_modal.rss_instructions": "Copiez le code ci-dessous et collez-le dans votre générateur RSS.",
|
||||||
|
"feed_claim_modal.rss_json_format": "Format JSON",
|
||||||
|
"feed_claim_modal.rss_xml_format": "Format XML",
|
||||||
|
"feed_claim_modal.rsshub_notice": "Ce flux est fourni par RSSHub avec un temps de cache d'1 heure. Veuillez prévoir jusqu'à 1 heure pour que les changements apparaissent après la publication du contenu.",
|
||||||
|
"feed_claim_modal.tab_content": "Contenu",
|
||||||
|
"feed_claim_modal.tab_description": "Description",
|
||||||
|
"feed_claim_modal.tab_rss": "Tag RSS",
|
||||||
|
"feed_claim_modal.title": "Réclamation de flux",
|
||||||
|
"feed_claim_modal.verify_ownership": "Pour réclamer ce flux comme le vôtre, vous devez vérifier la propriété.",
|
||||||
|
"feed_form.add_feed": "Ajouter un flux",
|
||||||
|
"feed_form.add_follow": "Ajouter un suivi",
|
||||||
|
"feed_form.category": "Catégorie",
|
||||||
|
"feed_form.category_description": "Par défaut, vos suivis seront groupés par site web.",
|
||||||
|
"feed_form.error_fetching_feed": "Erreur lors de la récupération du flux.",
|
||||||
|
"feed_form.fee": "Frais de suivi",
|
||||||
|
"feed_form.fee_description": "Pour suivre cette liste, vous devez payer des frais au créateur de la liste.",
|
||||||
|
"feed_form.feed_not_found": "Flux non trouvé.",
|
||||||
|
"feed_form.feedback": "Retour",
|
||||||
|
"feed_form.fill_default": "Remplir",
|
||||||
|
"feed_form.follow": "Suivre",
|
||||||
|
"feed_form.follow_with_fee": "Suivre avec {{fee}} Puissance",
|
||||||
|
"feed_form.followed": "🎉 Suivi.",
|
||||||
|
"feed_form.hide_from_timeline": "Masquer de la chronologie",
|
||||||
|
"feed_form.hide_from_timeline_description": "Si les entrées de cet abonnement sont visibles sur votre chronologie principale.",
|
||||||
|
"feed_form.private_follow": "Suivi privé",
|
||||||
|
"feed_form.private_follow_description": "Si ce suivi est visible publiquement sur votre page de profil.",
|
||||||
|
"feed_form.retry": "Réessayer",
|
||||||
|
"feed_form.title": "Titre",
|
||||||
|
"feed_form.title_description": "Titre personnalisé pour ce flux. Laissez vide pour utiliser le défaut.",
|
||||||
|
"feed_form.unfollow": "Ne plus suivre",
|
||||||
|
"feed_form.update": "Mettre à jour",
|
||||||
|
"feed_form.update_follow": "Mettre à jour le suivi",
|
||||||
|
"feed_form.updated": "🎉 Mis à jour.",
|
||||||
|
"feed_form.view": "Vue",
|
||||||
|
"feed_item.claimed_by_owner": "Ce flux est réclamé par",
|
||||||
|
"feed_item.claimed_by_unknown": "son propriétaire.",
|
||||||
|
"feed_item.claimed_by_you": "Réclamé par vous",
|
||||||
|
"feed_item.claimed_feed": "Flux réclamé",
|
||||||
|
"feed_item.claimed_list": "Liste réclamée",
|
||||||
|
"feed_item.error_since": "Erreur depuis",
|
||||||
|
"feed_item.not_publicly_visible": "Non visible publiquement sur votre page de profil",
|
||||||
|
"feed_item.onboarding_feed": "Ceci est un flux d'intégration",
|
||||||
|
"login.agree_to": "En continuant, vous acceptez nos",
|
||||||
|
"login.back": "Retour",
|
||||||
|
"login.confirm_password.label": "Confirmer le mot de passe",
|
||||||
|
"login.continueWith": "Continuer avec {{provider}}",
|
||||||
|
"login.email": "Email",
|
||||||
|
"login.enter_token": "Entrez le jeton d'autorisation pour continuer",
|
||||||
|
"login.forget_password.note": "Mot de passe oublié ?",
|
||||||
|
"login.have_account": "Vous avez déjà un compte ? <strong>Se connecter</strong>",
|
||||||
|
"login.lastUsed": "Dernier utilisé",
|
||||||
|
"login.magic_link_sent": "Lien magique envoyé ! Vérifiez votre email.",
|
||||||
|
"login.no_account": "Pas de compte ? <strong>S'inscrire</strong>",
|
||||||
|
"login.or": "OU",
|
||||||
|
"login.password": "Mot de passe",
|
||||||
|
"login.password_optional": "Optionnel",
|
||||||
|
"login.privacy": "Politique de confidentialité",
|
||||||
|
"login.send_magic_link": "Envoyer le lien magique",
|
||||||
|
"login.signUp": "S'inscrire avec un email",
|
||||||
|
"login.submit": "Soumettre",
|
||||||
|
"login.terms": "Conditions d'utilisation",
|
||||||
|
"login.title": "Bienvenue sur Folo",
|
||||||
|
"login.with_email.title": "Connexion avec Email",
|
||||||
|
"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 ?",
|
||||||
|
"mark_all_read_button.confirm_mark_all_info": "Confirmer tout marquer comme lu ?",
|
||||||
|
"mark_all_read_button.done": "Marqué",
|
||||||
|
"mark_all_read_button.mark_all_as_read": "Tout marquer comme lu",
|
||||||
|
"mark_all_read_button.mark_as_read": "Marquer <which /> comme lu",
|
||||||
|
"mark_all_read_button.undo": "Annuler",
|
||||||
|
"new_user_dialog.actions.close": "Fermer le dialogue",
|
||||||
|
"new_user_dialog.actions.finish": "Explorer !",
|
||||||
|
"new_user_dialog.ai.description": "Répondez à quelques questions et l'IA de Folo sélectionnera des flux, des listes et des plans de lecture pour vous.",
|
||||||
|
"new_user_dialog.ai.highlight_1": "Décrivez ce dont vous avez besoin en langage clair.",
|
||||||
|
"new_user_dialog.ai.highlight_2": "Recevez des sources recommandées avec le contexte de leur importance.",
|
||||||
|
"new_user_dialog.ai.highlight_3": "Continuez à discuter pour affiner les résumés, le ton et la fréquence.",
|
||||||
|
"new_user_dialog.ai.primary": "Lancer l'intégration IA",
|
||||||
|
"new_user_dialog.ai.title": "Laissez l'AI Copilot construire votre pile de départ",
|
||||||
|
"new_user_dialog.import.description": "Importez des exports OPML de n'importe quel lecteur et prévisualisez chaque flux avant de vous abonner.",
|
||||||
|
"new_user_dialog.import.highlight_1": "Téléchargez des fichiers OPML de Feedly, Inoreader ou tout autre lecteur RSS.",
|
||||||
|
"new_user_dialog.import.highlight_2": "Prévisualisez et choisissez les flux à conserver.",
|
||||||
|
"new_user_dialog.import.highlight_3": "Organisez immédiatement les flux importés dans des dossiers et des listes.",
|
||||||
|
"new_user_dialog.import.primary": "Importer OPML",
|
||||||
|
"new_user_dialog.import.title": "Apportez tout ce que vous suivez déjà",
|
||||||
|
"new_user_dialog.overview.description": "Folo est un lecteur RSS IA qui rassemble flux, newsletters, podcasts et mises à jour sociales dans un seul endroit ciblé.",
|
||||||
|
"new_user_dialog.overview.highlight_1": "Épinglez des flux, des listes et des sujets pour changer de contexte instantanément.",
|
||||||
|
"new_user_dialog.overview.highlight_2": "Utilisez la mise en page à deux colonnes pour parcourir les mises à jour tout en gardant l'entrée actuelle ouverte.",
|
||||||
|
"new_user_dialog.overview.highlight_3": "Les raccourcis clavier et les filtres vous maintiennent dans le flux.",
|
||||||
|
"new_user_dialog.overview.primary": "Parcourir Découvrir",
|
||||||
|
"new_user_dialog.overview.title": "Voyez tout ce qui vous intéresse dans une vue d'accueil calme",
|
||||||
|
"new_user_dialog.replay_video": "Relancer la vidéo",
|
||||||
|
"new_user_dialog.step_label.ai": "Étape 2 · AI Copilot",
|
||||||
|
"new_user_dialog.step_label.import": "Étape 3 · Importer",
|
||||||
|
"new_user_dialog.step_label.overview": "Étape 1 · Aperçu",
|
||||||
|
"new_user_dialog.title": "Bienvenue sur Folo",
|
||||||
|
"new_user_guide.actions.back": "Retour",
|
||||||
|
"new_user_guide.actions.finish": "Terminer",
|
||||||
|
"new_user_guide.actions.next": "Suivant",
|
||||||
|
"new_user_guide.ai_chat.intro": "Bienvenue sur Folo, le lecteur IA qui lit Internet pour vous.\n\nParlez-moi de vous.",
|
||||||
|
"new_user_guide.ai_chat.reroll": "Relancer",
|
||||||
|
"new_user_guide.ai_chat.suggestions.ai_regulation_learner": "J'apprends la réglementation de l'IA dans l'Union Européenne",
|
||||||
|
"new_user_guide.ai_chat.suggestions.climate_newsletter_writer": "J'écris une newsletter sur les technologies climatiques et j'ai besoin de sources quotidiennes",
|
||||||
|
"new_user_guide.ai_chat.suggestions.cybersecurity_tracker": "Je suis l'actualité de la cybersécurité et des vulnérabilités open-source",
|
||||||
|
"new_user_guide.ai_chat.suggestions.drug_delivery_student": "J'étudie l'administration de médicaments et je veux connaître les nouvelles approbations de la FDA",
|
||||||
|
"new_user_guide.ai_chat.suggestions.fashion_designer": "Je suis créateur de mode",
|
||||||
|
"new_user_guide.ai_chat.suggestions.investor_market_news": "Je suis investisseur et je dois suivre le marché boursier et les nouvelles des entreprises",
|
||||||
|
"new_user_guide.ai_chat.suggestions.japan_trip_planner": "Je prévois un voyage au Japon et je veux des conseils locaux",
|
||||||
|
"new_user_guide.ai_chat.suggestions.nano_engineering_researcher": "Je fais de la recherche en nano-ingénierie",
|
||||||
|
"new_user_guide.ai_chat.suggestions.nasa_fan": "Je suis fan de la NASA",
|
||||||
|
"new_user_guide.ai_chat.suggestions.personal_finance_builder": "Je construis un suivi de finances personnelles et j'ai besoin de meilleures pratiques",
|
||||||
|
"new_user_guide.ai_chat.suggestions.plant_based_cooking": "J'explore les tendances de la cuisine végétale",
|
||||||
|
"new_user_guide.ai_chat.suggestions.podcast_summary_seeker": "J'ai besoin de résumés pour de longs podcasts sur l'économie",
|
||||||
|
"new_user_guide.ai_chat.suggestions.robotics_coach": "J'entraîne une équipe de robotique de lycée et je cherche des idées de projets",
|
||||||
|
"new_user_guide.ai_chat.suggestions.saas_marketing_manager": "Je suis responsable marketing lançant un produit SaaS",
|
||||||
|
"new_user_guide.ai_chat.you_can_say": "Vous pouvez dire...",
|
||||||
|
"new_user_guide.confirm_skip.message": "Êtes-vous sûr de vouloir ignorer la configuration d'intégration ?",
|
||||||
|
"new_user_guide.confirm_skip.title": "Ignorer l'intégration ?",
|
||||||
|
"new_user_guide.intro.description": "Ce guide vous aidera à démarrer avec l'application.",
|
||||||
|
"new_user_guide.intro.title": "Lecture Vibe avec IA",
|
||||||
|
"new_user_guide.selection.empty_description": "Décrivez ce que vous recherchez dans le chat IA et nous vous recommanderons des flux.",
|
||||||
|
"new_user_guide.selection.empty_title": "Aucun flux sélectionné pour le moment",
|
||||||
|
"notify.store.default": "le magasin",
|
||||||
|
"notify.store.mas": "App Store",
|
||||||
|
"notify.store.mss": "Microsoft Store",
|
||||||
|
"notify.unfollow_feed": "<FeedItem /> ont été désabonnés.",
|
||||||
|
"notify.unfollow_feed_many": "Tous les flux sélectionnés ont été désabonnés.",
|
||||||
|
"notify.update_info": "{{app_name}} est prêt à être mis à jour !",
|
||||||
|
"notify.update_info_1": "Cliquez pour redémarrer",
|
||||||
|
"notify.update_info_2": "Cliquez pour recharger la page",
|
||||||
|
"notify.update_info_3": "Touchez pour recharger la page",
|
||||||
|
"notify.update_info_store": "Cliquez pour ouvrir {{store}}",
|
||||||
|
"player.back_10s": "Reculer 10s",
|
||||||
|
"player.close": "Fermer",
|
||||||
|
"player.download": "Télécharger",
|
||||||
|
"player.exit_full_screen": "Quitter Plein Écran",
|
||||||
|
"player.forward_10s": "Avancer 10s",
|
||||||
|
"player.full_screen": "Plein Écran",
|
||||||
|
"player.mute": "Muet",
|
||||||
|
"player.open_entry": "Ouvrir l'entrée",
|
||||||
|
"player.pause": "Pause",
|
||||||
|
"player.play": "Lecture",
|
||||||
|
"player.playback_rate": "Vitesse de lecture",
|
||||||
|
"player.unmute": "Rétablir le son",
|
||||||
|
"player.volume": "Volume",
|
||||||
|
"quick_add.placeholder": "Suivi rapide, tapez l'url du flux ici...",
|
||||||
|
"quick_add.title": "Suivi Rapide",
|
||||||
|
"register.confirm_password": "Confirmer le mot de passe",
|
||||||
|
"register.email": "Email",
|
||||||
|
"register.login": "Connexion",
|
||||||
|
"register.magic_link_sent": "Lien magique envoyé ! Vérifiez votre email.",
|
||||||
|
"register.password": "Mot de passe",
|
||||||
|
"register.password_optional": "Optionnel",
|
||||||
|
"register.referral.days": "Inscrivez-vous avec ce code de parrainage pour obtenir {{days}} jours d'Aperçu Pro",
|
||||||
|
"register.referral.description": "Inscrivez-vous avec un code de parrainage pour obtenir des jours supplémentaires d'Aperçu Pro.",
|
||||||
|
"register.referral.invalid": "Code de parrainage invalide",
|
||||||
|
"register.referral.label": "Code de parrainage",
|
||||||
|
"register.send_magic_link": "Envoyer le lien magique",
|
||||||
|
"register.submit": "Créer un compte",
|
||||||
|
"resize.tooltip.double_click_to_collapse": "<b>Double-cliquez</b> pour réinitialiser à la taille par défaut",
|
||||||
|
"resize.tooltip.drag_to_resize": "<b>Glisser</b> pour redimensionner",
|
||||||
|
"search.empty.no_results": "Aucun résultat trouvé.",
|
||||||
|
"search.group.entries": "Entrées",
|
||||||
|
"search.group.feeds": "Flux",
|
||||||
|
"search.options.all": "Tout",
|
||||||
|
"search.options.entry": "Entrée",
|
||||||
|
"search.options.feed": "Flux",
|
||||||
|
"search.options.search_type": "Type de recherche",
|
||||||
|
"search.placeholder": "Rechercher...",
|
||||||
|
"search.result_count_local_mode": "(Mode local)",
|
||||||
|
"search.tooltip.local_search": "Cette recherche couvre les données disponibles localement. Essayez un Rafraîchissement pour inclure les dernières données.",
|
||||||
|
"share.actions": "Actions",
|
||||||
|
"share.copy_failed": "Échec de la copie du lien",
|
||||||
|
"share.copy_link": "Copier le lien",
|
||||||
|
"share.default_description": "Découvrez cette entrée",
|
||||||
|
"share.default_title": "Partage d'entrée",
|
||||||
|
"share.discover_more": "Découvrez plus sur Folo",
|
||||||
|
"share.link_copied": "Lien copié dans le presse-papier",
|
||||||
|
"share.social_media": "Réseaux sociaux",
|
||||||
|
"share.system_share": "Partage système",
|
||||||
|
"share.title": "Partager l'entrée",
|
||||||
|
"shortcuts.guide.title": "Guide des raccourcis",
|
||||||
|
"sidebar.add_more_feeds": "Ajouter plus de flux",
|
||||||
|
"sidebar.already_on_discover_page": "Vous êtes déjà sur la page Découvrir ! Ajoutez le contenu que vous souhaitez suivre dans le panneau de droite.",
|
||||||
|
"sidebar.category_remove_dialog.cancel": "Annuler",
|
||||||
|
"sidebar.category_remove_dialog.continue": "Dissocier",
|
||||||
|
"sidebar.category_remove_dialog.description": "Les flux de cette catégorie seront dissociés et ramenés à leur groupement par défaut.",
|
||||||
|
"sidebar.category_remove_dialog.error": "Échec de la dissociation de la catégorie",
|
||||||
|
"sidebar.category_remove_dialog.success": "Catégorie dissociée avec succès",
|
||||||
|
"sidebar.category_remove_dialog.title": "Dissocier la catégorie",
|
||||||
|
"sidebar.category_unsubscribe_dialog.cancel": "Annuler",
|
||||||
|
"sidebar.category_unsubscribe_dialog.confirm": "Se désabonner de {{count}} flux",
|
||||||
|
"sidebar.category_unsubscribe_dialog.confirm_one": "Se désabonner d'1 flux",
|
||||||
|
"sidebar.category_unsubscribe_dialog.confirm_other": "Se désabonner de {{count}} flux",
|
||||||
|
"sidebar.category_unsubscribe_dialog.confirm_zero": "Se désabonner de 0 flux",
|
||||||
|
"sidebar.category_unsubscribe_dialog.description": "Vous êtes sur le point de vous désabonner de {{count}} flux dans {{category}}. Cette action est irréversible.",
|
||||||
|
"sidebar.category_unsubscribe_dialog.description_one": "Vous êtes sur le point de vous désabonner du seul flux dans {{category}}. Cette action est irréversible.",
|
||||||
|
"sidebar.category_unsubscribe_dialog.description_other": "Vous êtes sur le point de vous désabonner de {{count}} flux dans {{category}}. Cette action est irréversible.",
|
||||||
|
"sidebar.category_unsubscribe_dialog.description_zero": "Il n'y a aucun flux dans {{category}} dont se désabonner.",
|
||||||
|
"sidebar.category_unsubscribe_dialog.error": "Échec du désabonnement de la catégorie",
|
||||||
|
"sidebar.category_unsubscribe_dialog.success": "Désabonné de {{count}} flux dans {{category}}.",
|
||||||
|
"sidebar.category_unsubscribe_dialog.success_one": "Désabonné d'1 flux dans {{category}}.",
|
||||||
|
"sidebar.category_unsubscribe_dialog.success_other": "Désabonné de {{count}} flux dans {{category}}.",
|
||||||
|
"sidebar.category_unsubscribe_dialog.success_zero": "Aucun flux n'a été désabonné dans {{category}}.",
|
||||||
|
"sidebar.category_unsubscribe_dialog.title": "Se désabonner de {{folderName}}",
|
||||||
|
"sidebar.feed_actions.claim": "Réclamer",
|
||||||
|
"sidebar.feed_actions.claim_feed": "Réclamer le flux",
|
||||||
|
"sidebar.feed_actions.copy_email_address": "Copier l'adresse email",
|
||||||
|
"sidebar.feed_actions.copy_feed_badge": "Copier le badge du flux",
|
||||||
|
"sidebar.feed_actions.copy_feed_id": "Copier l'ID du flux",
|
||||||
|
"sidebar.feed_actions.copy_feed_url": "Copier l'URL du flux",
|
||||||
|
"sidebar.feed_actions.copy_list_id": "Copier l'ID de la liste",
|
||||||
|
"sidebar.feed_actions.copy_list_url": "Copier l'URL de la liste",
|
||||||
|
"sidebar.feed_actions.create_list": "Créer une nouvelle liste",
|
||||||
|
"sidebar.feed_actions.edit": "Modifier",
|
||||||
|
"sidebar.feed_actions.edit_feed": "Modifier le flux",
|
||||||
|
"sidebar.feed_actions.edit_inbox": "Modifier la boîte de réception",
|
||||||
|
"sidebar.feed_actions.edit_list": "Modifier la liste",
|
||||||
|
"sidebar.feed_actions.feed_owned_by_you": "Ce flux vous appartient",
|
||||||
|
"sidebar.feed_actions.list_owned_by_you": "Cette liste vous appartient",
|
||||||
|
"sidebar.feed_actions.mark_all_as_read": "Marquer comme lu",
|
||||||
|
"sidebar.feed_actions.navigate_to_feed": "Naviguer vers le flux",
|
||||||
|
"sidebar.feed_actions.navigate_to_list": "Naviguer vers la liste",
|
||||||
|
"sidebar.feed_actions.new_inbox": "Nouvelle boîte de réception",
|
||||||
|
"sidebar.feed_actions.open_feed_in_browser": "Ouvrir le flux dans {{which}}",
|
||||||
|
"sidebar.feed_actions.open_list_in_browser": "Ouvrir la liste dans {{which}}",
|
||||||
|
"sidebar.feed_actions.open_site_in_browser": "Ouvrir le site dans {{which}}",
|
||||||
|
"sidebar.feed_actions.reset_feed": "Réinitialiser le flux",
|
||||||
|
"sidebar.feed_actions.reset_feed_error": "Échec de la réinitialisation du flux.",
|
||||||
|
"sidebar.feed_actions.reset_feed_success": "Flux réinitialisé avec succès.",
|
||||||
|
"sidebar.feed_actions.resetting_feed": "Réinitialisation du flux...",
|
||||||
|
"sidebar.feed_actions.unfollow": "Ne plus suivre",
|
||||||
|
"sidebar.feed_actions.unfollow_feed": "Ne plus suivre le flux",
|
||||||
|
"sidebar.feed_actions.unfollow_feed_many": "Ne plus suivre tous les flux sélectionnés",
|
||||||
|
"sidebar.feed_actions.unfollow_feed_many_confirm": "Confirmer le désabonnement de tous les flux sélectionnés ?",
|
||||||
|
"sidebar.feed_actions.unfollow_feed_many_warning": "Attention : Cette opération désabonnera tous les flux sélectionnés et est irréversible.",
|
||||||
|
"sidebar.feed_column.context_menu.add_feeds_to_category": "Déplacer vers la catégorie",
|
||||||
|
"sidebar.feed_column.context_menu.add_feeds_to_list": "Ajouter des flux à la liste",
|
||||||
|
"sidebar.feed_column.context_menu.change_to_other_view": "Passer à une autre vue",
|
||||||
|
"sidebar.feed_column.context_menu.create_category": "Nouvelle catégorie",
|
||||||
|
"sidebar.feed_column.context_menu.mark_as_read": "Marquer comme lu",
|
||||||
|
"sidebar.feed_column.context_menu.new_category_modal.category_name": "Nom de la catégorie",
|
||||||
|
"sidebar.feed_column.context_menu.new_category_modal.create": "Créer",
|
||||||
|
"sidebar.feed_column.context_menu.rename_category": "Renommer la catégorie",
|
||||||
|
"sidebar.feed_column.context_menu.rename_category_error": "Échec du renommage de la catégorie",
|
||||||
|
"sidebar.feed_column.context_menu.rename_category_success": "Catégorie renommée avec succès",
|
||||||
|
"sidebar.feed_column.context_menu.title": "Déplacer vers une nouvelle catégorie",
|
||||||
|
"sidebar.feed_column.context_menu.ungroup_category": "Dissocier la catégorie",
|
||||||
|
"sidebar.feed_column.context_menu.ungroup_category_confirmation": "Dissocier la catégorie {{folderName}} ?",
|
||||||
|
"sidebar.feed_column.context_menu.unsubscribe_category": "Se désabonner de tout dans la catégorie",
|
||||||
|
"sidebar.select_sort_method": "Sélectionner une méthode de tri",
|
||||||
|
"sidebar.timeline_tabs.customize": "Personnaliser les onglets de vue...",
|
||||||
|
"sidebar.timeline_tabs.hide_tab": "Masquer cette vue",
|
||||||
|
"signin.continue_with": "Continuer avec {{provider}}",
|
||||||
|
"signin.sign_in_to": "Se connecter à",
|
||||||
|
"signin.sign_up_to": "S'inscrire à",
|
||||||
|
"subscription_limit_warning": "Limite d'abonnement atteinte : <br /><b>{{feedCount}}/{{feedLimit}} flux</b>, <br /><b>{{rsshubCount}}/{{rsshubLimit}} RSSHub</b>. <br /> Veuillez démarrer l'essai gratuit pour débloquer plus de quota ou nettoyer les flux inutilisés.",
|
||||||
|
"sync_indicator.disabled": "Pour des raisons de sécurité, la synchronisation est désactivée.",
|
||||||
|
"sync_indicator.offline": "Hors ligne",
|
||||||
|
"sync_indicator.synced": "Synchronisé avec le serveur",
|
||||||
|
"trending.entry": "Entrées tendance",
|
||||||
|
"trending.entry_no_results": "Aucune entrée tendance trouvée",
|
||||||
|
"trending.feed": "Flux tendance",
|
||||||
|
"trending.list": "Listes tendance",
|
||||||
|
"trending.user": "Utilisateurs tendance",
|
||||||
|
"tutorial.scroll_to_exit.description": "Faites défiler vers le haut lorsque vous êtes en haut pour revenir rapidement à la chronologie.",
|
||||||
|
"tutorial.scroll_to_exit.dismiss_hint": "Cliquez pour fermer",
|
||||||
|
"tutorial.scroll_to_exit.title": "Faites défiler pour revenir",
|
||||||
|
"user_button.account": "Compte",
|
||||||
|
"user_button.achievement": "Missions",
|
||||||
|
"user_button.actions": "Actions",
|
||||||
|
"user_button.ai": "IA",
|
||||||
|
"user_button.download_desktop_app": "Télécharger appli bureau",
|
||||||
|
"user_button.log_out": "Déconnexion",
|
||||||
|
"user_button.power": "Puissance",
|
||||||
|
"user_button.preferences": "Préférences",
|
||||||
|
"user_button.profile": "Profil",
|
||||||
|
"user_profile.about": "À propos",
|
||||||
|
"user_profile.close": "Fermer",
|
||||||
|
"user_profile.created_lists": "Listes créées",
|
||||||
|
"user_profile.edit": "Modifier",
|
||||||
|
"user_profile.loading": "Chargement",
|
||||||
|
"user_profile.share": "Partager",
|
||||||
|
"user_profile.subscriptions": "Abonnements",
|
||||||
|
"user_profile.toggle_item_style": "Basculer le style d'élément",
|
||||||
|
"words.achievement": "Réalisations",
|
||||||
|
"words.actions": "Actions",
|
||||||
|
"words.add": "Ajouter",
|
||||||
|
"words.all": "Tout",
|
||||||
|
"words.browser": "Navigateur",
|
||||||
|
"words.categories": "Catégories",
|
||||||
|
"words.confirm": "Confirmer",
|
||||||
|
"words.discover": "Découvrir",
|
||||||
|
"words.email": "Email",
|
||||||
|
"words.feeds": "Flux",
|
||||||
|
"words.import": "Importer",
|
||||||
|
"words.inbox": "Boîte de réception",
|
||||||
|
"words.items": "Éléments",
|
||||||
|
"words.language": "Langue",
|
||||||
|
"words.link": "Lien",
|
||||||
|
"words.lists": "Listes",
|
||||||
|
"words.load_archived_entries": "Charger les entrées archivées",
|
||||||
|
"words.login": "Connexion",
|
||||||
|
"words.mint": "Mint",
|
||||||
|
"words.newTab": "Nouvel onglet",
|
||||||
|
"words.power": "Puissance",
|
||||||
|
"words.rss": "RSS",
|
||||||
|
"words.rss3": "RSS3",
|
||||||
|
"words.rsshub": "RSSHub",
|
||||||
|
"words.search": "Recherche",
|
||||||
|
"words.show_more": "Afficher plus",
|
||||||
|
"words.starred": "Favoris",
|
||||||
|
"words.title": "Titre",
|
||||||
|
"words.transform": "Transformer",
|
||||||
|
"words.trending": "Tendance",
|
||||||
|
"words.undo": "Annuler",
|
||||||
|
"words.unread": "Non lu",
|
||||||
|
"words.user": "Utilisateur",
|
||||||
|
"words.view": "Vue",
|
||||||
|
"words.which.all": "tout",
|
||||||
|
"words.zero_items": "Zéro élément"
|
||||||
|
}
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
"activation.plan.title": "プランのアップグレード",
|
"activation.plan.title": "プランのアップグレード",
|
||||||
"activation.plan.upgrade": "アップグレード",
|
"activation.plan.upgrade": "アップグレード",
|
||||||
"activation.title": "招待コード",
|
"activation.title": "招待コード",
|
||||||
"ai.summary_not_available": "サマリーは利用できません",
|
"ai.summary_not_available": "要約は利用できません",
|
||||||
"ai.summary_upgrade_required_description": "Pro プランにアップグレードして、無制限の AI サマリー、翻訳、およびその他のインテリジェント機能をアンロックしましょう。",
|
"ai.summary_upgrade_required_description": "Pro プランにアップグレードして、無制限の AI サマリー、翻訳、およびその他のインテリジェント機能をアンロックしましょう。",
|
||||||
"ai.summary_upgrade_required_title": "AI サマリーを引き続き使用するにはプランをアップグレードしてください",
|
"ai.summary_upgrade_required_title": "AI サマリーを引き続き使用するにはプランをアップグレードしてください",
|
||||||
"ai.summary_upgrade_view_plans": "プランを表示",
|
"ai.summary_upgrade_view_plans": "プランを表示",
|
||||||
|
|
@ -73,6 +73,7 @@
|
||||||
"discover.inbox.description": "情報は email か webhooks を通して受信箱に配信されます。",
|
"discover.inbox.description": "情報は email か webhooks を通して受信箱に配信されます。",
|
||||||
"discover.inbox.email": "Email",
|
"discover.inbox.email": "Email",
|
||||||
"discover.inbox.handle": "ハンドル",
|
"discover.inbox.handle": "ハンドル",
|
||||||
|
"discover.inbox.no_inbox": "現在受信箱はありません。下のボタンをクリックして最初の受信箱を作成してください。",
|
||||||
"discover.inbox.secret": "シークレット",
|
"discover.inbox.secret": "シークレット",
|
||||||
"discover.inbox.title": "タイトル",
|
"discover.inbox.title": "タイトル",
|
||||||
"discover.inbox.webhooks_docs": "Webhooks ドキュメント",
|
"discover.inbox.webhooks_docs": "Webhooks ドキュメント",
|
||||||
|
|
@ -242,6 +243,8 @@
|
||||||
"feed_form.follow": "フォロー",
|
"feed_form.follow": "フォロー",
|
||||||
"feed_form.follow_with_fee": " {{fee}} Power で購読できます。",
|
"feed_form.follow_with_fee": " {{fee}} Power で購読できます。",
|
||||||
"feed_form.followed": "🎉 フォローしました。",
|
"feed_form.followed": "🎉 フォローしました。",
|
||||||
|
"feed_form.hide_from_timeline": "タイムラインから非表示",
|
||||||
|
"feed_form.hide_from_timeline_description": "このサブスクリプションのエントリーがメインビューのタイムラインに表示されるかどうか。",
|
||||||
"feed_form.private_follow": "プライベートフォロー",
|
"feed_form.private_follow": "プライベートフォロー",
|
||||||
"feed_form.private_follow_description": "このフォローがあなたのプロフィールページに公開されるかどうか。",
|
"feed_form.private_follow_description": "このフォローがあなたのプロフィールページに公開されるかどうか。",
|
||||||
"feed_form.retry": "再試行",
|
"feed_form.retry": "再試行",
|
||||||
|
|
@ -268,6 +271,7 @@
|
||||||
"login.enter_token": "認証トークンを入力して続行",
|
"login.enter_token": "認証トークンを入力して続行",
|
||||||
"login.forget_password.note": "パスワードをお忘れですか?",
|
"login.forget_password.note": "パスワードをお忘れですか?",
|
||||||
"login.have_account": "すでにアカウントをお持ちですか? <strong>サインイン</strong>",
|
"login.have_account": "すでにアカウントをお持ちですか? <strong>サインイン</strong>",
|
||||||
|
"login.lastUsed": "前回利用",
|
||||||
"login.magic_link_sent": "マジックリンクを送信しました!メールをご確認ください。",
|
"login.magic_link_sent": "マジックリンクを送信しました!メールをご確認ください。",
|
||||||
"login.no_account": "アカウントをお持ちでないですか? <strong>サインアップ</strong>",
|
"login.no_account": "アカウントをお持ちでないですか? <strong>サインアップ</strong>",
|
||||||
"login.or": "または",
|
"login.or": "または",
|
||||||
|
|
@ -289,6 +293,7 @@
|
||||||
"mark_all_read_button.mark_as_read": "<which />を既読にする",
|
"mark_all_read_button.mark_as_read": "<which />を既読にする",
|
||||||
"mark_all_read_button.undo": "元に戻す",
|
"mark_all_read_button.undo": "元に戻す",
|
||||||
"new_user_dialog.actions.close": "閉じる",
|
"new_user_dialog.actions.close": "閉じる",
|
||||||
|
"new_user_dialog.actions.finish": "探索を始める",
|
||||||
"new_user_dialog.ai.description": "いくつかの質問に答えるだけで、Folo の AI がフィードやリスト、読み方を提案します。",
|
"new_user_dialog.ai.description": "いくつかの質問に答えるだけで、Folo の AI がフィードやリスト、読み方を提案します。",
|
||||||
"new_user_dialog.ai.highlight_1": "知りたいことを自然な言葉で伝えるだけ。",
|
"new_user_dialog.ai.highlight_1": "知りたいことを自然な言葉で伝えるだけ。",
|
||||||
"new_user_dialog.ai.highlight_2": "おすすめの情報源とその理由が返ってきます。",
|
"new_user_dialog.ai.highlight_2": "おすすめの情報源とその理由が返ってきます。",
|
||||||
|
|
@ -487,6 +492,7 @@
|
||||||
"user_button.account": "アカウント",
|
"user_button.account": "アカウント",
|
||||||
"user_button.achievement": "実績",
|
"user_button.achievement": "実績",
|
||||||
"user_button.actions": "アクション",
|
"user_button.actions": "アクション",
|
||||||
|
"user_button.ai": "AI",
|
||||||
"user_button.download_desktop_app": "アプリをダウンロード",
|
"user_button.download_desktop_app": "アプリをダウンロード",
|
||||||
"user_button.log_out": "ログアウト",
|
"user_button.log_out": "ログアウト",
|
||||||
"user_button.power": "Power",
|
"user_button.power": "Power",
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,7 @@
|
||||||
"discover.inbox.description": "你可以通过邮件和 Webhook 在收件箱接收信息。",
|
"discover.inbox.description": "你可以通过邮件和 Webhook 在收件箱接收信息。",
|
||||||
"discover.inbox.email": "邮件地址",
|
"discover.inbox.email": "邮件地址",
|
||||||
"discover.inbox.handle": "唯一标识",
|
"discover.inbox.handle": "唯一标识",
|
||||||
|
"discover.inbox.no_inbox": "你目前没有收件箱,请点击下方按钮创建第一个收件箱。",
|
||||||
"discover.inbox.secret": "密钥",
|
"discover.inbox.secret": "密钥",
|
||||||
"discover.inbox.title": "标题",
|
"discover.inbox.title": "标题",
|
||||||
"discover.inbox.webhooks_docs": "Webhook 文档",
|
"discover.inbox.webhooks_docs": "Webhook 文档",
|
||||||
|
|
@ -491,6 +492,7 @@
|
||||||
"user_button.account": "账号",
|
"user_button.account": "账号",
|
||||||
"user_button.achievement": "成就",
|
"user_button.achievement": "成就",
|
||||||
"user_button.actions": "自动化",
|
"user_button.actions": "自动化",
|
||||||
|
"user_button.ai": "AI",
|
||||||
"user_button.download_desktop_app": "下载客户端",
|
"user_button.download_desktop_app": "下载客户端",
|
||||||
"user_button.log_out": "登出",
|
"user_button.log_out": "登出",
|
||||||
"user_button.power": "Power",
|
"user_button.power": "Power",
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,9 @@
|
||||||
"activation.plan.upgrade": "升級",
|
"activation.plan.upgrade": "升級",
|
||||||
"activation.title": "邀請碼",
|
"activation.title": "邀請碼",
|
||||||
"ai.summary_not_available": "暫無摘要",
|
"ai.summary_not_available": "暫無摘要",
|
||||||
|
"ai.summary_upgrade_required_description": "升級到 Pro 方案,解鎖無限 AI 摘要、翻譯與更多智慧功能。",
|
||||||
|
"ai.summary_upgrade_required_title": "開啟免費試用以繼續使用 AI 摘要",
|
||||||
|
"ai.summary_upgrade_view_plans": "查看方案",
|
||||||
"app.copy_logo_svg": "複製 Logo SVG",
|
"app.copy_logo_svg": "複製 Logo SVG",
|
||||||
"app.copy_logo_text_svg": "複製圖標 SVG 文字",
|
"app.copy_logo_text_svg": "複製圖標 SVG 文字",
|
||||||
"app.toggle_sidebar": "切換側邊欄",
|
"app.toggle_sidebar": "切換側邊欄",
|
||||||
|
|
@ -99,6 +102,13 @@
|
||||||
"discover.target.feeds": "RSS 摘要",
|
"discover.target.feeds": "RSS 摘要",
|
||||||
"discover.target.label": "搜尋",
|
"discover.target.label": "搜尋",
|
||||||
"discover.target.lists": "列表",
|
"discover.target.lists": "列表",
|
||||||
|
"discover.tips.auto_detect": "自動辨識輸入類型",
|
||||||
|
"discover.tips.search_keyword": "支援關鍵字、URL、RSS 訂閱源或 RSSHub 路由",
|
||||||
|
"discover.tools.description": "更多工具",
|
||||||
|
"discover.tools.import": "匯入 OPML",
|
||||||
|
"discover.tools.inbox": "收件匣",
|
||||||
|
"discover.tools.transform": "HTML 轉 RSS",
|
||||||
|
"discover.tools.user": "追蹤使用者",
|
||||||
"entry.click_to_return": "向上捲動或點擊此處返回",
|
"entry.click_to_return": "向上捲動或點擊此處返回",
|
||||||
"entry.exit_detail": "返回時間軸",
|
"entry.exit_detail": "返回時間軸",
|
||||||
"entry.scroll_up_to_exit": "向上捲動返回時間軸",
|
"entry.scroll_up_to_exit": "向上捲動返回時間軸",
|
||||||
|
|
@ -203,6 +213,7 @@
|
||||||
"feed.followsAndFeeds": "在 {{appName}} 上有 {{subscriptionCount}} 個 {{subscriptionNoun}} 和 {{feedsCount}} 個 {{feedsNoun}}",
|
"feed.followsAndFeeds": "在 {{appName}} 上有 {{subscriptionCount}} 個 {{subscriptionNoun}} 和 {{feedsCount}} 個 {{feedsNoun}}",
|
||||||
"feed.read_one": "閱讀",
|
"feed.read_one": "閱讀",
|
||||||
"feed.read_other": "閱讀",
|
"feed.read_other": "閱讀",
|
||||||
|
"feed_category.onboarding_feed": "此分類包含入門引導訂閱源",
|
||||||
"feed_claim_modal.choose_verification_method": "有三種驗證方式,您可以選擇其中一種進行驗證。",
|
"feed_claim_modal.choose_verification_method": "有三種驗證方式,您可以選擇其中一種進行驗證。",
|
||||||
"feed_claim_modal.claim_button": "認領",
|
"feed_claim_modal.claim_button": "認領",
|
||||||
"feed_claim_modal.content_instructions": "複製以下內容,發佈到需要驗證的 RSS 摘要。",
|
"feed_claim_modal.content_instructions": "複製以下內容,發佈到需要驗證的 RSS 摘要。",
|
||||||
|
|
@ -251,6 +262,7 @@
|
||||||
"feed_item.claimed_list": "已認領列表",
|
"feed_item.claimed_list": "已認領列表",
|
||||||
"feed_item.error_since": "RSS 摘要失效:",
|
"feed_item.error_since": "RSS 摘要失效:",
|
||||||
"feed_item.not_publicly_visible": "未公開顯示於您的個人頁面",
|
"feed_item.not_publicly_visible": "未公開顯示於您的個人頁面",
|
||||||
|
"feed_item.onboarding_feed": "這是一個入門引導訂閱源",
|
||||||
"login.agree_to": "繼續即表示您同意我們的",
|
"login.agree_to": "繼續即表示您同意我們的",
|
||||||
"login.back": "返回",
|
"login.back": "返回",
|
||||||
"login.confirm_password.label": "確認密碼",
|
"login.confirm_password.label": "確認密碼",
|
||||||
|
|
@ -281,6 +293,7 @@
|
||||||
"mark_all_read_button.mark_as_read": "標記 <which /> 為已讀",
|
"mark_all_read_button.mark_as_read": "標記 <which /> 為已讀",
|
||||||
"mark_all_read_button.undo": "復原",
|
"mark_all_read_button.undo": "復原",
|
||||||
"new_user_dialog.actions.close": "關閉",
|
"new_user_dialog.actions.close": "關閉",
|
||||||
|
"new_user_dialog.actions.finish": "立即探索",
|
||||||
"new_user_dialog.ai.description": "回答幾個提示,Folo 的 AI 會幫你挑選訂閱、清單與閱讀計畫。",
|
"new_user_dialog.ai.description": "回答幾個提示,Folo 的 AI 會幫你挑選訂閱、清單與閱讀計畫。",
|
||||||
"new_user_dialog.ai.highlight_1": "用自然語言描述你的需求。",
|
"new_user_dialog.ai.highlight_1": "用自然語言描述你的需求。",
|
||||||
"new_user_dialog.ai.highlight_2": "收到附上推薦理由的精選來源。",
|
"new_user_dialog.ai.highlight_2": "收到附上推薦理由的精選來源。",
|
||||||
|
|
@ -459,9 +472,12 @@
|
||||||
"sidebar.feed_column.context_menu.ungroup_category_confirmation": "取消分類「{{folderName}}」的分組?",
|
"sidebar.feed_column.context_menu.ungroup_category_confirmation": "取消分類「{{folderName}}」的分組?",
|
||||||
"sidebar.feed_column.context_menu.unsubscribe_category": "取消分類內所有訂閱",
|
"sidebar.feed_column.context_menu.unsubscribe_category": "取消分類內所有訂閱",
|
||||||
"sidebar.select_sort_method": "選擇排序方式",
|
"sidebar.select_sort_method": "選擇排序方式",
|
||||||
|
"sidebar.timeline_tabs.customize": "自訂檢視分頁...",
|
||||||
|
"sidebar.timeline_tabs.hide_tab": "隱藏此檢視",
|
||||||
"signin.continue_with": "透過 {{provider}} 登入",
|
"signin.continue_with": "透過 {{provider}} 登入",
|
||||||
"signin.sign_in_to": "登入",
|
"signin.sign_in_to": "登入",
|
||||||
"signin.sign_up_to": "註冊",
|
"signin.sign_up_to": "註冊",
|
||||||
|
"subscription_limit_warning": "訂閱數量已達上限:<br /><b>{{feedCount}}/{{feedLimit}} 個訂閱源</b>,<br /><b>{{rsshubCount}}/{{rsshubLimit}} 個 RSSHub</b>。<br />請開啟免費試用以解鎖更多配額,或清理未使用的訂閱源。",
|
||||||
"sync_indicator.disabled": "由於安全原因,同步功能已停用。",
|
"sync_indicator.disabled": "由於安全原因,同步功能已停用。",
|
||||||
"sync_indicator.offline": "離線",
|
"sync_indicator.offline": "離線",
|
||||||
"sync_indicator.synced": "已與伺服器同步",
|
"sync_indicator.synced": "已與伺服器同步",
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,19 @@
|
||||||
"discover.category.traditional-media": "News",
|
"discover.category.traditional-media": "News",
|
||||||
"discover.category.travel": "Travel",
|
"discover.category.travel": "Travel",
|
||||||
"discover.category.university": "University",
|
"discover.category.university": "University",
|
||||||
|
"discover.empty.no_content": "No content found in this category.",
|
||||||
|
"discover.empty.try_another_category_or_language": "Try selecting another category or language.",
|
||||||
|
"discover.search.results_one": "Found {{count}} result",
|
||||||
|
"discover.search.results_other": "Found {{count}} results",
|
||||||
|
"discover.search.results_zero": "No results found.",
|
||||||
|
"error_screen.crashed": "{{appName}} crashed!",
|
||||||
|
"error_screen.list_try_later": "There was a problem loading the list. Please try again later.",
|
||||||
|
"error_screen.list_unable_to_load": "Unable to load content",
|
||||||
|
"error_screen.page_failed": "This page went wrong, go back and try again.",
|
||||||
|
"error_screen.unexpected": "An unexpected error occurred.",
|
||||||
|
"error_screen.unknown": "Unknown error",
|
||||||
|
"feed.actions.follow": "Follow",
|
||||||
|
"feed.actions.followed": "Followed",
|
||||||
"feed.entry_week_one": "{{count}} entry/week",
|
"feed.entry_week_one": "{{count}} entry/week",
|
||||||
"feed.entry_week_other": "{{count}} entries/week",
|
"feed.entry_week_other": "{{count}} entries/week",
|
||||||
"feed.follower_one": "follower",
|
"feed.follower_one": "follower",
|
||||||
|
|
@ -38,6 +51,7 @@
|
||||||
"ok": "OK",
|
"ok": "OK",
|
||||||
"quantifier.piece": "",
|
"quantifier.piece": "",
|
||||||
"retry": "Retry",
|
"retry": "Retry",
|
||||||
|
"search.empty.no_results": "No results found.",
|
||||||
"space": " ",
|
"space": " ",
|
||||||
"time.last_night": "Last Night",
|
"time.last_night": "Last Night",
|
||||||
"time.the_night_before_last": "The Night Before Last",
|
"time.the_night_before_last": "The Night Before Last",
|
||||||
|
|
@ -49,6 +63,7 @@
|
||||||
"words.all": "All",
|
"words.all": "All",
|
||||||
"words.back": "Back",
|
"words.back": "Back",
|
||||||
"words.cancel": "Cancel",
|
"words.cancel": "Cancel",
|
||||||
|
"words.categories": "Categories",
|
||||||
"words.chinese": "Chinese",
|
"words.chinese": "Chinese",
|
||||||
"words.close": "Close",
|
"words.close": "Close",
|
||||||
"words.confirm": "Confirm",
|
"words.confirm": "Confirm",
|
||||||
|
|
@ -62,9 +77,11 @@
|
||||||
"words.english": "English",
|
"words.english": "English",
|
||||||
"words.entry": "Entry",
|
"words.entry": "Entry",
|
||||||
"words.expand": "Expand",
|
"words.expand": "Expand",
|
||||||
|
"words.feedback": "Feedback",
|
||||||
"words.feeds": "Feeds",
|
"words.feeds": "Feeds",
|
||||||
"words.finishSetup": "Finish Setup",
|
"words.finishSetup": "Finish Setup",
|
||||||
"words.follow": "Follow",
|
"words.follow": "Follow",
|
||||||
|
"words.french": "French",
|
||||||
"words.id": "ID",
|
"words.id": "ID",
|
||||||
"words.inbox": "Inbox",
|
"words.inbox": "Inbox",
|
||||||
"words.items_one": "Item",
|
"words.items_one": "Item",
|
||||||
|
|
@ -86,6 +103,7 @@
|
||||||
"words.search": "Search",
|
"words.search": "Search",
|
||||||
"words.starred": "Starred",
|
"words.starred": "Starred",
|
||||||
"words.submit": "Submit",
|
"words.submit": "Submit",
|
||||||
|
"words.trending": "Trending",
|
||||||
"words.unsaved_changes": "Unsaved changes",
|
"words.unsaved_changes": "Unsaved changes",
|
||||||
"words.update": "Update",
|
"words.update": "Update",
|
||||||
"words.which.above": "above",
|
"words.which.above": "above",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,111 @@
|
||||||
|
{
|
||||||
|
"app.copied_to_clipboard": "Copié dans le presse-papier",
|
||||||
|
"discover.category.all": "Populaire",
|
||||||
|
"discover.category.anime": "Anime / Manga",
|
||||||
|
"discover.category.bbs": "Forum",
|
||||||
|
"discover.category.blog": "Blog",
|
||||||
|
"discover.category.design": "Design",
|
||||||
|
"discover.category.finance": "Finance",
|
||||||
|
"discover.category.forecast": "Prévisions",
|
||||||
|
"discover.category.game": "Jeux vidéo",
|
||||||
|
"discover.category.government": "Gouvernement",
|
||||||
|
"discover.category.journal": "Journal scientifique",
|
||||||
|
"discover.category.live": "Direct",
|
||||||
|
"discover.category.multimedia": "Multimédia",
|
||||||
|
"discover.category.new-media": "Nouveaux médias",
|
||||||
|
"discover.category.picture": "Images",
|
||||||
|
"discover.category.program-update": "Mises à jour logicielles",
|
||||||
|
"discover.category.programming": "Programmation",
|
||||||
|
"discover.category.reading": "Lecture",
|
||||||
|
"discover.category.shopping": "Shopping",
|
||||||
|
"discover.category.social-media": "Réseaux sociaux",
|
||||||
|
"discover.category.study": "Études",
|
||||||
|
"discover.category.traditional-media": "Actualités",
|
||||||
|
"discover.category.travel": "Voyage",
|
||||||
|
"discover.category.university": "Université",
|
||||||
|
"discover.empty.no_content": "Aucun contenu trouvé dans cette catégorie.",
|
||||||
|
"discover.empty.try_another_category_or_language": "Essayez de sélectionner une autre catégorie ou langue.",
|
||||||
|
"discover.search.results_one": "{{count}} résultat trouvé",
|
||||||
|
"discover.search.results_other": "{{count}} résultats trouvés",
|
||||||
|
"discover.search.results_zero": "Aucun résultat trouvé.",
|
||||||
|
"error_screen.crashed": "{{appName}} a planté !",
|
||||||
|
"error_screen.list_try_later": "Un problème est survenu lors du chargement de la liste. Veuillez réessayer plus tard.",
|
||||||
|
"error_screen.list_unable_to_load": "Impossible de charger le contenu",
|
||||||
|
"error_screen.page_failed": "Cette page a rencontré un problème. Revenez en arrière et réessayez.",
|
||||||
|
"error_screen.unexpected": "Une erreur inattendue s'est produite.",
|
||||||
|
"error_screen.unknown": "Erreur inconnue",
|
||||||
|
"feed.actions.follow": "Suivre",
|
||||||
|
"feed.actions.followed": "Suivi",
|
||||||
|
"feed.entry_week_one": "{{count}} article/semaine",
|
||||||
|
"feed.entry_week_other": "{{count}} articles/semaine",
|
||||||
|
"feed.follower_one": "abonné",
|
||||||
|
"feed.follower_other": "abonnés",
|
||||||
|
"feed.updated_at": "Mis à jour",
|
||||||
|
"feed_view_type.all": "Tout",
|
||||||
|
"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": "",
|
||||||
|
"retry": "Réessayer",
|
||||||
|
"search.empty.no_results": "Aucun résultat trouvé.",
|
||||||
|
"space": " ",
|
||||||
|
"time.last_night": "Hier soir",
|
||||||
|
"time.the_night_before_last": "Avant-hier soir",
|
||||||
|
"time.today": "Aujourd'hui",
|
||||||
|
"time.yesterday": "Hier",
|
||||||
|
"tips.load-lng-error": "Échec du chargement du pack de langue",
|
||||||
|
"words.actions": "Actions",
|
||||||
|
"words.ago": "il y a",
|
||||||
|
"words.all": "Tout",
|
||||||
|
"words.back": "Retour",
|
||||||
|
"words.cancel": "Annuler",
|
||||||
|
"words.categories": "Catégories",
|
||||||
|
"words.chinese": "Chinois",
|
||||||
|
"words.close": "Fermer",
|
||||||
|
"words.confirm": "Confirmer",
|
||||||
|
"words.copy": "Copier",
|
||||||
|
"words.create": "Créer",
|
||||||
|
"words.default": "Défaut",
|
||||||
|
"words.delete": "Supprimer",
|
||||||
|
"words.documentation": "Documentation",
|
||||||
|
"words.download": "Télécharger",
|
||||||
|
"words.edit": "Modifier",
|
||||||
|
"words.english": "Anglais",
|
||||||
|
"words.entry": "Entrée",
|
||||||
|
"words.expand": "Développer",
|
||||||
|
"words.feedback": "Feedback",
|
||||||
|
"words.feeds": "Flux",
|
||||||
|
"words.finishSetup": "Terminer l'installation",
|
||||||
|
"words.follow": "S'abonner",
|
||||||
|
"words.french": "Français",
|
||||||
|
"words.id": "ID",
|
||||||
|
"words.inbox": "Boîte de réception",
|
||||||
|
"words.items_one": "Élément",
|
||||||
|
"words.items_other": "Éléments",
|
||||||
|
"words.letsGo": "C'est parti",
|
||||||
|
"words.lists": "Listes",
|
||||||
|
"words.local": "local",
|
||||||
|
"words.manage": "gérer",
|
||||||
|
"words.next": "Suivant",
|
||||||
|
"words.record": "enregistrement",
|
||||||
|
"words.record_one": "enregistrement",
|
||||||
|
"words.record_other": "enregistrements",
|
||||||
|
"words.reset": "Réinitialiser",
|
||||||
|
"words.result": "résultat",
|
||||||
|
"words.result_one": "résultat",
|
||||||
|
"words.result_other": "résultats",
|
||||||
|
"words.rsshub": "RSSHub",
|
||||||
|
"words.save": "Enregistrer",
|
||||||
|
"words.search": "Rechercher",
|
||||||
|
"words.starred": "Favoris",
|
||||||
|
"words.submit": "Soumettre",
|
||||||
|
"words.trending": "Tendance",
|
||||||
|
"words.unsaved_changes": "Modifications non enregistrées",
|
||||||
|
"words.update": "Mettre à jour",
|
||||||
|
"words.which.above": "ci-dessus",
|
||||||
|
"words.which.all": "tout"
|
||||||
|
}
|
||||||
|
|
@ -23,6 +23,19 @@
|
||||||
"discover.category.traditional-media": "ニュース",
|
"discover.category.traditional-media": "ニュース",
|
||||||
"discover.category.travel": "旅行",
|
"discover.category.travel": "旅行",
|
||||||
"discover.category.university": "大学",
|
"discover.category.university": "大学",
|
||||||
|
"discover.empty.no_content": "このカテゴリにはコンテンツがありません。",
|
||||||
|
"discover.empty.try_another_category_or_language": "別のカテゴリまたは言語を選択してください。",
|
||||||
|
"discover.search.results_one": "{{count}} 件の結果が見つかりました",
|
||||||
|
"discover.search.results_other": "{{count}} 件の結果が見つかりました",
|
||||||
|
"discover.search.results_zero": "結果が見つかりませんでした。",
|
||||||
|
"error_screen.crashed": "{{appName}} がクラッシュしました。",
|
||||||
|
"error_screen.list_try_later": "リストの読み込み中に問題が発生しました。しばらくしてから再試行してください。",
|
||||||
|
"error_screen.list_unable_to_load": "コンテンツを読み込めません",
|
||||||
|
"error_screen.page_failed": "このページで問題が発生しました。戻ってもう一度お試しください。",
|
||||||
|
"error_screen.unexpected": "予期しないエラーが発生しました。",
|
||||||
|
"error_screen.unknown": "不明なエラー",
|
||||||
|
"feed.actions.follow": "フォロー",
|
||||||
|
"feed.actions.followed": "フォロー済み",
|
||||||
"feed.entry_week_one": "{{count}} エントリー/週",
|
"feed.entry_week_one": "{{count}} エントリー/週",
|
||||||
"feed.entry_week_other": "{{count}} エントリー/週",
|
"feed.entry_week_other": "{{count}} エントリー/週",
|
||||||
"feed.follower_one": "フォロワー",
|
"feed.follower_one": "フォロワー",
|
||||||
|
|
@ -38,6 +51,7 @@
|
||||||
"ok": "OK",
|
"ok": "OK",
|
||||||
"quantifier.piece": "個",
|
"quantifier.piece": "個",
|
||||||
"retry": "再試行",
|
"retry": "再試行",
|
||||||
|
"search.empty.no_results": "結果が見つかりませんでした。",
|
||||||
"space": "",
|
"space": "",
|
||||||
"time.last_night": "昨夜",
|
"time.last_night": "昨夜",
|
||||||
"time.the_night_before_last": "一昨夜",
|
"time.the_night_before_last": "一昨夜",
|
||||||
|
|
@ -49,6 +63,7 @@
|
||||||
"words.all": "すべて",
|
"words.all": "すべて",
|
||||||
"words.back": "戻る",
|
"words.back": "戻る",
|
||||||
"words.cancel": "キャンセル",
|
"words.cancel": "キャンセル",
|
||||||
|
"words.categories": "カテゴリー",
|
||||||
"words.chinese": "中国語",
|
"words.chinese": "中国語",
|
||||||
"words.close": "閉じる",
|
"words.close": "閉じる",
|
||||||
"words.confirm": "確認",
|
"words.confirm": "確認",
|
||||||
|
|
@ -62,9 +77,11 @@
|
||||||
"words.english": "英語",
|
"words.english": "英語",
|
||||||
"words.entry": "エントリー",
|
"words.entry": "エントリー",
|
||||||
"words.expand": "展開",
|
"words.expand": "展開",
|
||||||
|
"words.feedback": "フィードバック",
|
||||||
"words.feeds": "フィード",
|
"words.feeds": "フィード",
|
||||||
"words.finishSetup": "セットアップ完了",
|
"words.finishSetup": "セットアップ完了",
|
||||||
"words.follow": "フォロー",
|
"words.follow": "フォロー",
|
||||||
|
"words.french": "フランス語",
|
||||||
"words.id": "ID",
|
"words.id": "ID",
|
||||||
"words.inbox": "受信トレイ",
|
"words.inbox": "受信トレイ",
|
||||||
"words.items_one": "アイテム",
|
"words.items_one": "アイテム",
|
||||||
|
|
@ -86,6 +103,8 @@
|
||||||
"words.search": "検索",
|
"words.search": "検索",
|
||||||
"words.starred": "スター付き",
|
"words.starred": "スター付き",
|
||||||
"words.submit": "送信",
|
"words.submit": "送信",
|
||||||
|
"words.trending": "トレンド",
|
||||||
|
"words.unsaved_changes": "未保存の変更",
|
||||||
"words.update": "更新",
|
"words.update": "更新",
|
||||||
"words.which.above": "上記",
|
"words.which.above": "上記",
|
||||||
"words.which.all": "すべて"
|
"words.which.all": "すべて"
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,19 @@
|
||||||
"discover.category.traditional-media": "传统媒体",
|
"discover.category.traditional-media": "传统媒体",
|
||||||
"discover.category.travel": "出行旅游",
|
"discover.category.travel": "出行旅游",
|
||||||
"discover.category.university": "大学通知",
|
"discover.category.university": "大学通知",
|
||||||
|
"discover.empty.no_content": "此分类下暂无内容",
|
||||||
|
"discover.empty.try_another_category_or_language": "请尝试选择其他分类或语言",
|
||||||
|
"discover.search.results_one": "找到 {{count}} 条结果",
|
||||||
|
"discover.search.results_other": "找到 {{count}} 条结果",
|
||||||
|
"discover.search.results_zero": "搜索结果为空",
|
||||||
|
"error_screen.crashed": "{{appName}} 崩溃了!",
|
||||||
|
"error_screen.list_try_later": "加载列表时出现问题,请稍后重试。",
|
||||||
|
"error_screen.list_unable_to_load": "无法加载内容",
|
||||||
|
"error_screen.page_failed": "此页面出现异常,请返回后重试。",
|
||||||
|
"error_screen.unexpected": "发生了未知错误。",
|
||||||
|
"error_screen.unknown": "未知错误",
|
||||||
|
"feed.actions.follow": "订阅",
|
||||||
|
"feed.actions.followed": "已订阅",
|
||||||
"feed.entry_week_one": "{{count}} 条目/周",
|
"feed.entry_week_one": "{{count}} 条目/周",
|
||||||
"feed.entry_week_other": "{{count}} 条目/周",
|
"feed.entry_week_other": "{{count}} 条目/周",
|
||||||
"feed.follower_one": "订阅者",
|
"feed.follower_one": "订阅者",
|
||||||
|
|
@ -38,6 +51,7 @@
|
||||||
"ok": "好",
|
"ok": "好",
|
||||||
"quantifier.piece": "条",
|
"quantifier.piece": "条",
|
||||||
"retry": "重试",
|
"retry": "重试",
|
||||||
|
"search.empty.no_results": "搜索结果为空",
|
||||||
"space": "",
|
"space": "",
|
||||||
"time.last_night": "昨晚",
|
"time.last_night": "昨晚",
|
||||||
"time.the_night_before_last": "前天晚上",
|
"time.the_night_before_last": "前天晚上",
|
||||||
|
|
@ -49,6 +63,7 @@
|
||||||
"words.all": "全部",
|
"words.all": "全部",
|
||||||
"words.back": "返回",
|
"words.back": "返回",
|
||||||
"words.cancel": "取消",
|
"words.cancel": "取消",
|
||||||
|
"words.categories": "分类",
|
||||||
"words.chinese": "中文",
|
"words.chinese": "中文",
|
||||||
"words.close": "关闭",
|
"words.close": "关闭",
|
||||||
"words.confirm": "确认",
|
"words.confirm": "确认",
|
||||||
|
|
@ -62,9 +77,11 @@
|
||||||
"words.english": "英语",
|
"words.english": "英语",
|
||||||
"words.entry": "条目",
|
"words.entry": "条目",
|
||||||
"words.expand": "展开",
|
"words.expand": "展开",
|
||||||
|
"words.feedback": "反馈",
|
||||||
"words.feeds": "订阅源",
|
"words.feeds": "订阅源",
|
||||||
"words.finishSetup": "完成设置",
|
"words.finishSetup": "完成设置",
|
||||||
"words.follow": "订阅",
|
"words.follow": "订阅",
|
||||||
|
"words.french": "法语",
|
||||||
"words.id": "ID",
|
"words.id": "ID",
|
||||||
"words.inbox": "收件箱",
|
"words.inbox": "收件箱",
|
||||||
"words.items_one": "内容",
|
"words.items_one": "内容",
|
||||||
|
|
@ -86,6 +103,8 @@
|
||||||
"words.search": "搜索",
|
"words.search": "搜索",
|
||||||
"words.starred": "收藏",
|
"words.starred": "收藏",
|
||||||
"words.submit": "提交",
|
"words.submit": "提交",
|
||||||
|
"words.trending": "趋势",
|
||||||
|
"words.unsaved_changes": "未保存的更改",
|
||||||
"words.update": "更新",
|
"words.update": "更新",
|
||||||
"words.which.above": "以上",
|
"words.which.above": "以上",
|
||||||
"words.which.all": "全部"
|
"words.which.all": "全部"
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,19 @@
|
||||||
"discover.category.traditional-media": "傳統媒體",
|
"discover.category.traditional-media": "傳統媒體",
|
||||||
"discover.category.travel": "出行旅遊",
|
"discover.category.travel": "出行旅遊",
|
||||||
"discover.category.university": "大學資訊",
|
"discover.category.university": "大學資訊",
|
||||||
|
"discover.empty.no_content": "此分類下暫無內容",
|
||||||
|
"discover.empty.try_another_category_or_language": "請嘗試選擇其他分類或語言",
|
||||||
|
"discover.search.results_one": "找到 {{count}} 筆結果",
|
||||||
|
"discover.search.results_other": "找到 {{count}} 筆結果",
|
||||||
|
"discover.search.results_zero": "搜尋結果為空",
|
||||||
|
"error_screen.crashed": "{{appName}} 當機了!",
|
||||||
|
"error_screen.list_try_later": "載入列表時發生問題,請稍後再試。",
|
||||||
|
"error_screen.list_unable_to_load": "無法載入內容",
|
||||||
|
"error_screen.page_failed": "此頁面發生錯誤,請返回後重試。",
|
||||||
|
"error_screen.unexpected": "發生未預期的錯誤。",
|
||||||
|
"error_screen.unknown": "未知錯誤",
|
||||||
|
"feed.actions.follow": "跟隨",
|
||||||
|
"feed.actions.followed": "已跟隨",
|
||||||
"feed.entry_week_one": "{{count}} 條目/週",
|
"feed.entry_week_one": "{{count}} 條目/週",
|
||||||
"feed.entry_week_other": "{{count}} 條目/週",
|
"feed.entry_week_other": "{{count}} 條目/週",
|
||||||
"feed.follower_one": "跟隨者",
|
"feed.follower_one": "跟隨者",
|
||||||
|
|
@ -38,6 +51,7 @@
|
||||||
"ok": "確定",
|
"ok": "確定",
|
||||||
"quantifier.piece": "條",
|
"quantifier.piece": "條",
|
||||||
"retry": "重試",
|
"retry": "重試",
|
||||||
|
"search.empty.no_results": "搜尋結果為空",
|
||||||
"space": "",
|
"space": "",
|
||||||
"time.last_night": "昨晚",
|
"time.last_night": "昨晚",
|
||||||
"time.the_night_before_last": "前晚",
|
"time.the_night_before_last": "前晚",
|
||||||
|
|
@ -49,6 +63,7 @@
|
||||||
"words.all": "全部",
|
"words.all": "全部",
|
||||||
"words.back": "返回",
|
"words.back": "返回",
|
||||||
"words.cancel": "取消",
|
"words.cancel": "取消",
|
||||||
|
"words.categories": "分類",
|
||||||
"words.chinese": "中文",
|
"words.chinese": "中文",
|
||||||
"words.close": "關閉",
|
"words.close": "關閉",
|
||||||
"words.confirm": "確認",
|
"words.confirm": "確認",
|
||||||
|
|
@ -62,9 +77,11 @@
|
||||||
"words.english": "英文",
|
"words.english": "英文",
|
||||||
"words.entry": "條目",
|
"words.entry": "條目",
|
||||||
"words.expand": "展開",
|
"words.expand": "展開",
|
||||||
|
"words.feedback": "回饋",
|
||||||
"words.feeds": "RSS 摘要",
|
"words.feeds": "RSS 摘要",
|
||||||
"words.finishSetup": "完成設置",
|
"words.finishSetup": "完成設置",
|
||||||
"words.follow": "跟隨",
|
"words.follow": "跟隨",
|
||||||
|
"words.french": "法文",
|
||||||
"words.id": "ID",
|
"words.id": "ID",
|
||||||
"words.inbox": "收件匣",
|
"words.inbox": "收件匣",
|
||||||
"words.items_one": "內容",
|
"words.items_one": "內容",
|
||||||
|
|
@ -86,6 +103,8 @@
|
||||||
"words.search": "搜尋",
|
"words.search": "搜尋",
|
||||||
"words.starred": "收藏",
|
"words.starred": "收藏",
|
||||||
"words.submit": "提交",
|
"words.submit": "提交",
|
||||||
|
"words.trending": "趨勢",
|
||||||
|
"words.unsaved_changes": "尚未儲存的變更",
|
||||||
"words.update": "更新",
|
"words.update": "更新",
|
||||||
"words.which.above": "以上",
|
"words.which.above": "以上",
|
||||||
"words.which.all": "全部"
|
"words.which.all": "全部"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
{
|
||||||
|
"1": "L'opération précédente n'est pas terminée",
|
||||||
|
"3": "Contenu non traitable",
|
||||||
|
"4": "Entrée invalide",
|
||||||
|
"5": "Configuration manquante",
|
||||||
|
"6": "Introuvable",
|
||||||
|
"1000": "Non autorisé",
|
||||||
|
"1001": "Échec de la création de session",
|
||||||
|
"1002": "Paramètre invalide",
|
||||||
|
"1003": "Invitation invalide",
|
||||||
|
"1004": "Aucune permission",
|
||||||
|
"1005": "Erreur interne",
|
||||||
|
"1006": "Email non vérifié",
|
||||||
|
"1007": "Échec de la vérification du captcha",
|
||||||
|
"2000": "Seuls les administrateurs peuvent actualiser les flux",
|
||||||
|
"2001": "Flux introuvable",
|
||||||
|
"2002": "feedId ou url requis",
|
||||||
|
"2003": "Erreur de récupération du flux",
|
||||||
|
"2004": "Échec de l'analyse du flux",
|
||||||
|
"2010": "Échec de la vérification de propriété",
|
||||||
|
"2011": "Limite d'abonnement dépassée",
|
||||||
|
"3000": "Entrée introuvable",
|
||||||
|
"4000": "Déjà réclamé",
|
||||||
|
"4001": "Erreur de portefeuille utilisateur",
|
||||||
|
"4002": "Solde insuffisant",
|
||||||
|
"4003": "Solde retirable du flux insuffisant",
|
||||||
|
"4004": "Erreur de portefeuille utilisateur cible",
|
||||||
|
"4005": "Calcul de la puissance quotidienne en cours",
|
||||||
|
"4006": "Montant de boost invalide",
|
||||||
|
"4007": "Code à deux facteurs invalide",
|
||||||
|
"4008": "Code à deux facteurs requis",
|
||||||
|
"4010": "Airdrop non éligible",
|
||||||
|
"4011": "Envoi de l'airdrop en cours",
|
||||||
|
"4012": "Airdrop déjà envoyé",
|
||||||
|
"4013": "Airdrop non vérifié",
|
||||||
|
"5000": "Limite d'invitation dépassée. Veuillez réessayer dans quelques jours.",
|
||||||
|
"5001": "L'invitation existe déjà.",
|
||||||
|
"5002": "Code d'invitation déjà utilisé.",
|
||||||
|
"5003": "Le code d'invitation n'existe pas.",
|
||||||
|
"6000": "Utilisateur introuvable",
|
||||||
|
"7000": "Paramètre introuvable",
|
||||||
|
"7001": "Onglet de paramètre invalide",
|
||||||
|
"7002": "Charge utile de paramètre invalide",
|
||||||
|
"7003": "Charge utile de paramètre trop volumineuse",
|
||||||
|
"8000": "Liste introuvable",
|
||||||
|
"8001": "Permission de liste refusée",
|
||||||
|
"8002": "Limite de liste dépassée",
|
||||||
|
"8003": "Flux déjà ajouté à la liste",
|
||||||
|
"8004": "Impossible de supprimer une liste avec des abonnements.",
|
||||||
|
"9000": "Succès non terminé",
|
||||||
|
"9001": "Succès déjà reçu",
|
||||||
|
"9002": "Succès en cours d'audit",
|
||||||
|
"9003": "Audit de succès non nécessaire",
|
||||||
|
"10000": "Boîte de réception introuvable",
|
||||||
|
"10001": "La boîte de réception existe déjà",
|
||||||
|
"10002": "Limite de boîte de réception dépassée",
|
||||||
|
"10003": "Permission de boîte de réception refusée",
|
||||||
|
"12000": "Limite d'action dépassée",
|
||||||
|
"13000": "Route RSSHub introuvable",
|
||||||
|
"13001": "Vous n'êtes pas le propriétaire de cette instance RSSHub",
|
||||||
|
"13002": "RSSHub est en cours d'utilisation",
|
||||||
|
"13003": "RSSHub introuvable",
|
||||||
|
"13004": "Limite d'abonnement RSSHub dépassée",
|
||||||
|
"13005": "Achat RSSHub introuvable",
|
||||||
|
"13006": "Configuration RSSHub invalide",
|
||||||
|
"13007": "Cette instance RSSHub est actuellement indisponible",
|
||||||
|
"14000": "Fichier invalide",
|
||||||
|
"14001": "Fichier trop volumineux",
|
||||||
|
"14002": "Échec du téléchargement",
|
||||||
|
"15000": "Limite de jetons IA dépassée. Veuillez réessayer plus tard.",
|
||||||
|
"15001": "Vous n'êtes pas autorisé à accéder au chat IA",
|
||||||
|
"15002": "Limite de fichier joint IA dépassée. Veuillez réessayer plus tard.",
|
||||||
|
"15003": "Mémoire IA introuvable",
|
||||||
|
"15004": "Limite de mémoire IA dépassée",
|
||||||
|
"15005": "Crédits IA insuffisants pour accéder à cette fonctionnalité.",
|
||||||
|
"16000": "Erreur OAuth MCP",
|
||||||
|
"16001": "Erreur de découverte MCP",
|
||||||
|
"16002": "Service MCP introuvable",
|
||||||
|
"16003": "État OAuth invalide ou expiré",
|
||||||
|
"16004": "Échec de l'échange de jeton",
|
||||||
|
"16005": "Service MCP utilisateur introuvable",
|
||||||
|
"16006": "Erreur de configuration MCP",
|
||||||
|
"16007": "Échec de l'exécution de l'outil MCP",
|
||||||
|
"17000": "Limite d'abonnement aux flux max pour utilisateur d'essai dépassée",
|
||||||
|
"17001": "Limite d'abonnement aux listes max pour utilisateur d'essai dépassée",
|
||||||
|
"17002": "Limite d'abonnement aux boîtes de réception max pour utilisateur d'essai dépassée",
|
||||||
|
"17003": "Aucune permission pour l'utilisateur gratuit"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
{
|
||||||
|
"copied_link": "Lien copié dans le presse-papiers",
|
||||||
|
"feed.actions.follow": "S'abonner",
|
||||||
|
"feed.actions.followed": "Abonné",
|
||||||
|
"feed.actions.open": "Ouvrir dans {{which}}",
|
||||||
|
"feed.copy_feed_url": "Copier l'URL du flux",
|
||||||
|
"feed.entry_week_one": "{{count}} entrée/semaine",
|
||||||
|
"feed.entry_week_other": "{{count}} entrées/semaine",
|
||||||
|
"feed.feeds_one": "flux",
|
||||||
|
"feed.feeds_other": "flux",
|
||||||
|
"feed.follow_to_view_all": "Abonnez-vous pour voir tous les {{count}} flux...",
|
||||||
|
"feed.follower_one": "abonné",
|
||||||
|
"feed.follower_other": "abonnés",
|
||||||
|
"feed.followsAndFeeds": "{{subscriptionCount}} {{subscriptionNoun}} et {{feedsCount}} {{feedsNoun}} sur {{appName}}",
|
||||||
|
"feed.madeby": "Fait par",
|
||||||
|
"feed.preview": "Aperçu",
|
||||||
|
"feed.read_one": "lu",
|
||||||
|
"feed.read_other": "lus",
|
||||||
|
"feed.updated_at": "Mis à jour",
|
||||||
|
"feed.view_feed_url": "Voir l'URL du flux",
|
||||||
|
"feed_item.claimed_by_owner": "Ce flux est revendiqué par",
|
||||||
|
"feed_item.claimed_by_unknown": "son propriétaire.",
|
||||||
|
"feed_item.claimed_by_you": "Revendiqué par vous",
|
||||||
|
"feed_item.claimed_feed": "Flux revendiqué",
|
||||||
|
"feed_item.claimed_list": "Liste revendiquée",
|
||||||
|
"feed_item.error_since": "Erreur depuis",
|
||||||
|
"feed_item.not_publicly_visible": "Non visible publiquement sur votre page de profil",
|
||||||
|
"header.app": "App",
|
||||||
|
"header.download": "Télécharger",
|
||||||
|
"invitation.activate": "Activer",
|
||||||
|
"invitation.codeOptions.1": "Cherchez un utilisateur alpha test pour vous inviter.",
|
||||||
|
"invitation.codeOptions.2": "Rejoignez notre serveur Discord pour des giveaways occasionnels.",
|
||||||
|
"invitation.codeOptions.3": "Suivez notre compte X pour des giveaways occasionnels.",
|
||||||
|
"invitation.earlyAccess": "Folo nécessite actuellement un code d'invitation.",
|
||||||
|
"invitation.earlyAccessMessage": "😰 Désolé, Folo nécessite actuellement un code d'invitation pour être utilisé.",
|
||||||
|
"invitation.generateButton": "Générer un nouveau code",
|
||||||
|
"invitation.generateCost": "Vous pouvez dépenser {{INVITATION_PRICE}} Power pour générer un code d'invitation pour vos amis.",
|
||||||
|
"invitation.getCodeMessage": "Vous pouvez obtenir un code d'invitation des manières suivantes :",
|
||||||
|
"invitation.title": "Code d'invitation",
|
||||||
|
"login.backToWebApp": "Retour à l'application Web",
|
||||||
|
"login.confirm_password.label": "Confirmer le mot de passe",
|
||||||
|
"login.continueWith": "Continuer avec {{provider}}",
|
||||||
|
"login.email": "E-mail",
|
||||||
|
"login.enter_token": "Si vous avez installé le client mais n'êtes pas redirigé, copiez le jeton ci-dessous et collez-le dans le formulaire \"Entrez le jeton d'autorisation pour continuer\" sur le client (v0.5.0+).",
|
||||||
|
"login.errors.unknown": "Erreurs inconnues",
|
||||||
|
"login.forget_password.description": "Entrez l'adresse e-mail associée à votre compte et nous vous enverrons un e-mail expliquant comment réinitialiser votre mot de passe.",
|
||||||
|
"login.forget_password.email_invalid": "E-mail invalide",
|
||||||
|
"login.forget_password.email_required": "E-mail requis",
|
||||||
|
"login.forget_password.label": "Mot de passe oublié",
|
||||||
|
"login.forget_password.note": "Mot de passe oublié ?",
|
||||||
|
"login.forget_password.success": "L'e-mail a été envoyé avec succès",
|
||||||
|
"login.have_account": "Vous avez déjà un compte ? <strong>Se connecter</strong>",
|
||||||
|
"login.lastUsed": "Dernière utilisation",
|
||||||
|
"login.logInTo": "Se connecter à",
|
||||||
|
"login.logInWithEmail": "Se connecter avec un e-mail",
|
||||||
|
"login.new_password.label": "Nouveau mot de passe",
|
||||||
|
"login.no_account": "Vous n'avez pas de compte ? <strong>S'inscrire</strong>",
|
||||||
|
"login.no_client": "Aucun client détecté, vous pouvez <weblink>continuer à utiliser l'application Web</weblink>.",
|
||||||
|
"login.openApp": "Ouvrir l'application",
|
||||||
|
"login.or": "Ou",
|
||||||
|
"login.password": "Mot de passe",
|
||||||
|
"login.redirecting": "Redirection",
|
||||||
|
"login.register": "En créer un",
|
||||||
|
"login.reset_password.description": "Entrez un nouveau mot de passe et confirmez-le pour réinitialiser votre mot de passe.",
|
||||||
|
"login.reset_password.label": "Réinitialiser le mot de passe",
|
||||||
|
"login.reset_password.success": "Le mot de passe a été réinitialisé avec succès",
|
||||||
|
"login.signOut": "Se déconnecter",
|
||||||
|
"login.signUp": "S'inscrire avec un e-mail",
|
||||||
|
"login.signUpTo": "S'inscrire à",
|
||||||
|
"login.submit": "Soumettre",
|
||||||
|
"login.two_factor.code": "Code d'authentification à deux facteurs",
|
||||||
|
"login.two_factor.verify": "Terminer la vérification",
|
||||||
|
"login.welcomeTo": "Bienvenue sur ",
|
||||||
|
"redirect.continueInBrowser": "Continuer dans le navigateur",
|
||||||
|
"redirect.instruction": "Il est maintenant temps d'ouvrir {{app_name}} et de fermer cette page en toute sécurité.",
|
||||||
|
"redirect.openApp": "Ouvrir {{app_name}}",
|
||||||
|
"redirect.successMessage": "Vous vous êtes connecté avec succès au compte {{app_name}}.",
|
||||||
|
"register.confirm_password": "Confirmer le mot de passe",
|
||||||
|
"register.email": "E-mail",
|
||||||
|
"register.label": "S'inscrire à {{app_name}}",
|
||||||
|
"register.login": "Se connecter",
|
||||||
|
"register.password": "Mot de passe",
|
||||||
|
"register.referral.days": "Inscrivez-vous avec ce code de parrainage pour obtenir {{days}} jours d'essai Pro",
|
||||||
|
"register.referral.description": "Inscrivez-vous avec un code de parrainage pour obtenir des jours supplémentaires d'essai Pro.",
|
||||||
|
"register.referral.invalid": "Code de parrainage invalide",
|
||||||
|
"register.referral.label": "Code de parrainage",
|
||||||
|
"register.submit": "Créer un compte",
|
||||||
|
"words.email": "E-mail"
|
||||||
|
}
|
||||||
|
|
@ -50,6 +50,7 @@
|
||||||
"login.forget_password.note": "パスワードを忘れましたか?",
|
"login.forget_password.note": "パスワードを忘れましたか?",
|
||||||
"login.forget_password.success": "メールの送信に成功しました。",
|
"login.forget_password.success": "メールの送信に成功しました。",
|
||||||
"login.have_account": "すでにアカウントをお持ちですか? <strong>サインイン</strong>",
|
"login.have_account": "すでにアカウントをお持ちですか? <strong>サインイン</strong>",
|
||||||
|
"login.lastUsed": "前回使用",
|
||||||
"login.logInTo": "ログイン ",
|
"login.logInTo": "ログイン ",
|
||||||
"login.logInWithEmail": "メールでログイン",
|
"login.logInWithEmail": "メールでログイン",
|
||||||
"login.new_password.label": "新しいパスワード",
|
"login.new_password.label": "新しいパスワード",
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,10 @@
|
||||||
"register.label": "创建 {{app_name}} 账户",
|
"register.label": "创建 {{app_name}} 账户",
|
||||||
"register.login": "登录",
|
"register.login": "登录",
|
||||||
"register.password": "密码",
|
"register.password": "密码",
|
||||||
|
"register.referral.days": "使用此推荐码注册可获得 {{days}} 天 Pro 预览",
|
||||||
|
"register.referral.description": "使用推荐码注册可获得额外的 Pro 预览天数。",
|
||||||
|
"register.referral.invalid": "无效的推荐码",
|
||||||
|
"register.referral.label": "推荐码",
|
||||||
"register.submit": "创建账户",
|
"register.submit": "创建账户",
|
||||||
"words.email": "邮件地址"
|
"words.email": "邮件地址"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"name": "Français (France)"
|
||||||
|
}
|
||||||
|
|
@ -5,10 +5,25 @@
|
||||||
"ai.summary_upgrade_view_plans": "View Plans",
|
"ai.summary_upgrade_view_plans": "View Plans",
|
||||||
"entry.pull_up_to_next_entry": "Pull up to go to the next entry",
|
"entry.pull_up_to_next_entry": "Pull up to go to the next entry",
|
||||||
"entry.release_to_next_entry": "Release to go to the next entry",
|
"entry.release_to_next_entry": "Release to go to the next entry",
|
||||||
|
"entry_actions.toggle_ai_summary": "AI Summary",
|
||||||
|
"entry_content.no_content": "No media available",
|
||||||
|
"entry_content.no_video_url": "No video URL found",
|
||||||
|
"entry_list.zero_unread": "Zero Unread",
|
||||||
|
"login.agree_to": "By continuing, you agree to our",
|
||||||
"login.back": "Back",
|
"login.back": "Back",
|
||||||
|
"login.confirm_password.label": "Confirm Password",
|
||||||
"login.continueWith": "Continue with {{provider}}",
|
"login.continueWith": "Continue with {{provider}}",
|
||||||
|
"login.email": "Email",
|
||||||
|
"login.forget_password.note": "Forgot your password?",
|
||||||
"login.have_account": "Already have an account? <strong>Sign in</strong>",
|
"login.have_account": "Already have an account? <strong>Sign in</strong>",
|
||||||
|
"login.invalid_email_or_password": "Invalid email or password",
|
||||||
"login.no_account": "Don't have an account? <strong>Sign up</strong>",
|
"login.no_account": "Don't have an account? <strong>Sign up</strong>",
|
||||||
|
"login.password": "Password",
|
||||||
|
"login.passwords_do_not_match": "Passwords don't match",
|
||||||
|
"login.privacy": "Privacy Policy",
|
||||||
|
"login.sign_up_successful": "Sign up successful",
|
||||||
|
"login.submit": "Submit",
|
||||||
|
"login.terms": "Terms of Service",
|
||||||
"onboarding.edit_profile": "Edit Profile",
|
"onboarding.edit_profile": "Edit Profile",
|
||||||
"onboarding.edit_profile_description": "Change your name, email, and profile picture",
|
"onboarding.edit_profile_description": "Change your name, email, and profile picture",
|
||||||
"onboarding.finished_description": "You have completed the guide. Enjoy your journey!",
|
"onboarding.finished_description": "You have completed the guide. Enjoy your journey!",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,96 @@
|
||||||
|
{
|
||||||
|
"ai.summary_not_available": "Résumé non disponible",
|
||||||
|
"ai.summary_upgrade_required_description": "Débloquez des résumés, des traductions et des fonctionnalités intelligentes illimités avec le plan Pro.",
|
||||||
|
"ai.summary_upgrade_required_title": "Commencez l'essai gratuit pour continuer à utiliser le résumé IA",
|
||||||
|
"ai.summary_upgrade_view_plans": "Voir les plans",
|
||||||
|
"entry.pull_up_to_next_entry": "Tirez vers le haut pour passer à l'entrée suivante",
|
||||||
|
"entry.release_to_next_entry": "Relâchez pour passer à l'entrée suivante",
|
||||||
|
"entry_actions.toggle_ai_summary": "Résumé IA",
|
||||||
|
"entry_content.no_content": "Aucun média disponible",
|
||||||
|
"entry_content.no_video_url": "Aucune URL vidéo trouvée",
|
||||||
|
"entry_list.zero_unread": "Zéro non lu",
|
||||||
|
"login.agree_to": "En continuant, vous acceptez nos",
|
||||||
|
"login.back": "Retour",
|
||||||
|
"login.confirm_password.label": "Confirmer le mot de passe",
|
||||||
|
"login.continueWith": "Continuer avec {{provider}}",
|
||||||
|
"login.email": "Email",
|
||||||
|
"login.forget_password.note": "Mot de passe oublié ?",
|
||||||
|
"login.have_account": "Vous avez déjà un compte ? <strong>Se connecter</strong>",
|
||||||
|
"login.invalid_email_or_password": "Email ou mot de passe invalide",
|
||||||
|
"login.no_account": "Vous n'avez pas de compte ? <strong>S'inscrire</strong>",
|
||||||
|
"login.password": "Mot de passe",
|
||||||
|
"login.passwords_do_not_match": "Les mots de passe ne correspondent pas",
|
||||||
|
"login.privacy": "Politique de confidentialité",
|
||||||
|
"login.sign_up_successful": "Inscription réussie",
|
||||||
|
"login.submit": "Soumettre",
|
||||||
|
"login.terms": "Conditions d'utilisation",
|
||||||
|
"onboarding.edit_profile": "Modifier le profil",
|
||||||
|
"onboarding.edit_profile_description": "Modifiez votre nom, votre email et votre photo de profil",
|
||||||
|
"onboarding.finished_description": "Vous avez terminé le guide. Profitez de votre expérience !",
|
||||||
|
"onboarding.finished_title": "Tout est prêt !",
|
||||||
|
"onboarding.import_content": "Importer votre contenu",
|
||||||
|
"onboarding.import_description": "Si vous avez déjà utilisé RSS, vous pouvez importer un fichier OPML",
|
||||||
|
"onboarding.interests_description": "Abonnez-vous aux flux qui correspondent à vos intérêts.",
|
||||||
|
"onboarding.interests_title": "Découvrez vos intérêts",
|
||||||
|
"onboarding.language_description": "Choisissez la langue que vous souhaitez utiliser dans Folo",
|
||||||
|
"onboarding.preferences_description": "Définissez vos préférences pour que Folo fonctionne au mieux pour vous. Vous pourrez toujours les modifier plus tard dans les paramètres.",
|
||||||
|
"onboarding.preferences_title": "Personnalisez votre expérience",
|
||||||
|
"onboarding.reading_balanced": "Équilibré : $t(onboarding.reading_balanced_description)",
|
||||||
|
"onboarding.reading_balanced_description": "Marquer automatiquement les entrées comme lues lorsqu'elles ne sont plus visibles",
|
||||||
|
"onboarding.reading_conservative": "Conservateur : $t(onboarding.reading_conservative_description)",
|
||||||
|
"onboarding.reading_conservative_description": "Marquer les entrées comme lues uniquement lors d'un clic",
|
||||||
|
"onboarding.reading_preferences": "Préférences de lecture",
|
||||||
|
"onboarding.reading_radical": "Radical : $t(onboarding.reading_radical_description)",
|
||||||
|
"onboarding.reading_radical_description": "Marquer automatiquement les entrées comme lues lorsqu'elles sont affichées",
|
||||||
|
"onboarding.shuffle": "Mélanger",
|
||||||
|
"onboarding.suggestions_feed": "Flux de suggestions",
|
||||||
|
"onboarding.welcome_guide": "Ce guide vous aidera à démarrer avec l'application.",
|
||||||
|
"onboarding.welcome_title": "Bienvenue sur Folo !",
|
||||||
|
"operation.add_feeds_to_category": "Déplacer vers la catégorie",
|
||||||
|
"operation.change_to_other_view": "Passer à une autre vue",
|
||||||
|
"operation.copy.email_address": "Adresse e-mail",
|
||||||
|
"operation.copy.link": "Lien",
|
||||||
|
"operation.copy_which": "Copier {{which}}",
|
||||||
|
"operation.copy_which_success": "{{which}} copié dans le presse-papiers",
|
||||||
|
"operation.delete_category": "Supprimer la catégorie",
|
||||||
|
"operation.delete_category_confirm": "Cette opération supprimera votre catégorie, mais les flux qu'elle contient seront conservés et regroupés par site web.",
|
||||||
|
"operation.delete_category_which": "Supprimer la catégorie {{category}}",
|
||||||
|
"operation.edit": "Modifier",
|
||||||
|
"operation.enter_new_name_for_category": "Entrez un nouveau nom pour {{category}}",
|
||||||
|
"operation.error_since": "Erreur depuis",
|
||||||
|
"operation.mark_all_as_read": "Tout marquer comme lu",
|
||||||
|
"operation.mark_all_as_read_confirm": "Confirmer tout marquer comme lu ?",
|
||||||
|
"operation.mark_all_as_read_which": "Marquer {{which}} comme lu",
|
||||||
|
"operation.mark_all_as_read_which_above": "Au-dessus",
|
||||||
|
"operation.mark_all_as_read_which_below": "En dessous",
|
||||||
|
"operation.mark_as_read": "Marquer comme lu",
|
||||||
|
"operation.mark_as_unread": "Marquer comme non lu",
|
||||||
|
"operation.open_link": "Ouvrir le lien",
|
||||||
|
"operation.rename_category": "Renommer la catégorie",
|
||||||
|
"operation.share": "Partager",
|
||||||
|
"operation.show_error_message": "Afficher le message d'erreur",
|
||||||
|
"operation.star": "Favoris",
|
||||||
|
"operation.toggle_unread_only.show_all.label": "Tout afficher",
|
||||||
|
"operation.toggle_unread_only.show_all.success": "Affichage de toutes les entrées",
|
||||||
|
"operation.toggle_unread_only.show_unread_only.label": "Afficher uniquement les non lus",
|
||||||
|
"operation.toggle_unread_only.show_unread_only.success": "Affichage des entrées non lues",
|
||||||
|
"operation.unfollow": "Se désabonner",
|
||||||
|
"operation.unstar": "Retirer des favoris",
|
||||||
|
"profile.title": "Profil de {{name}}",
|
||||||
|
"profile.uncategorized_feeds": "Flux non classés",
|
||||||
|
"signin.sign_in_to": "Se connecter à",
|
||||||
|
"signin.sign_up_to": "S'inscrire à",
|
||||||
|
"subscription_form.category": "Catégorie",
|
||||||
|
"subscription_form.category_description": "Par défaut, vos abonnements seront regroupés par site web.",
|
||||||
|
"subscription_form.hide_from_timeline": "Masquer de la timeline",
|
||||||
|
"subscription_form.hide_from_timeline_description": "Si les entrées de cet abonnement sont visibles sur votre timeline principale.",
|
||||||
|
"subscription_form.private_follow": "Suivi privé",
|
||||||
|
"subscription_form.private_follow_description": "Si ce suivi est visible publiquement sur votre page de profil.",
|
||||||
|
"subscription_form.title": "Titre",
|
||||||
|
"subscription_form.title_description": "Titre personnalisé pour ce flux. Laissez vide pour utiliser celui par défaut.",
|
||||||
|
"subscription_form.view": "Vue",
|
||||||
|
"tabs.discover": "Découvrir",
|
||||||
|
"tabs.home": "Accueil",
|
||||||
|
"tabs.settings": "Paramètres",
|
||||||
|
"tabs.subscriptions": "Abonnements"
|
||||||
|
}
|
||||||
|
|
@ -5,10 +5,25 @@
|
||||||
"ai.summary_upgrade_view_plans": "プランを表示",
|
"ai.summary_upgrade_view_plans": "プランを表示",
|
||||||
"entry.pull_up_to_next_entry": "引っ張って次のエントリーに移動",
|
"entry.pull_up_to_next_entry": "引っ張って次のエントリーに移動",
|
||||||
"entry.release_to_next_entry": "離して次のエントリーに移動",
|
"entry.release_to_next_entry": "離して次のエントリーに移動",
|
||||||
|
"entry_actions.toggle_ai_summary": "AI 要約に切り替え",
|
||||||
|
"entry_content.no_content": "コンテンツがありません",
|
||||||
|
"entry_content.no_video_url": "動画 URL が見つかりません",
|
||||||
|
"entry_list.zero_unread": "未読ゼロ",
|
||||||
|
"login.agree_to": " 続行することで、あなたは私たちの",
|
||||||
"login.back": "戻る",
|
"login.back": "戻る",
|
||||||
|
"login.confirm_password.label": "パスワードの確認",
|
||||||
"login.continueWith": "{{provider}} で続行",
|
"login.continueWith": "{{provider}} で続行",
|
||||||
|
"login.email": "Email",
|
||||||
|
"login.forget_password.note": "パスワードをお忘れですか?",
|
||||||
"login.have_account": "すでにアカウントをお持ちですか? <strong>サインイン</strong>",
|
"login.have_account": "すでにアカウントをお持ちですか? <strong>サインイン</strong>",
|
||||||
|
"login.invalid_email_or_password": "メールアドレスまたはパスワードが正しくありません",
|
||||||
"login.no_account": "アカウントをお持ちでないですか? <strong>サインアップ</strong>",
|
"login.no_account": "アカウントをお持ちでないですか? <strong>サインアップ</strong>",
|
||||||
|
"login.password": "パスワード",
|
||||||
|
"login.passwords_do_not_match": "パスワードが一致しません",
|
||||||
|
"login.privacy": "プライバシーポリシー",
|
||||||
|
"login.sign_up_successful": "サインアップに成功しました",
|
||||||
|
"login.submit": "ログイン",
|
||||||
|
"login.terms": "利用規約",
|
||||||
"onboarding.edit_profile": "プロフィールを編集",
|
"onboarding.edit_profile": "プロフィールを編集",
|
||||||
"onboarding.edit_profile_description": "名前、メールアドレス、プロフィール写真を変更します",
|
"onboarding.edit_profile_description": "名前、メールアドレス、プロフィール写真を変更します",
|
||||||
"onboarding.finished_description": "ガイドを完了しました。旅をお楽しみください!",
|
"onboarding.finished_description": "ガイドを完了しました。旅をお楽しみください!",
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,25 @@
|
||||||
"ai.summary_upgrade_view_plans": "查看计划",
|
"ai.summary_upgrade_view_plans": "查看计划",
|
||||||
"entry.pull_up_to_next_entry": "继续上拉查看下一条内容",
|
"entry.pull_up_to_next_entry": "继续上拉查看下一条内容",
|
||||||
"entry.release_to_next_entry": "松开查看下一条内容",
|
"entry.release_to_next_entry": "松开查看下一条内容",
|
||||||
|
"entry_actions.toggle_ai_summary": "切换 AI 总结",
|
||||||
|
"entry_content.no_content": "没有内容",
|
||||||
|
"entry_content.no_video_url": "未找到视频链接",
|
||||||
|
"entry_list.zero_unread": "全部已读",
|
||||||
|
"login.agree_to": "继续即表示您同意我们的",
|
||||||
"login.back": "返回",
|
"login.back": "返回",
|
||||||
|
"login.confirm_password.label": "确认密码",
|
||||||
"login.continueWith": "使用 {{provider}} 继续",
|
"login.continueWith": "使用 {{provider}} 继续",
|
||||||
|
"login.email": "邮件地址",
|
||||||
|
"login.forget_password.note": "忘记了密码?",
|
||||||
"login.have_account": "已有账户?<strong>登录</strong>",
|
"login.have_account": "已有账户?<strong>登录</strong>",
|
||||||
|
"login.invalid_email_or_password": "邮箱或密码无效",
|
||||||
"login.no_account": "没有账户?<strong>注册</strong>",
|
"login.no_account": "没有账户?<strong>注册</strong>",
|
||||||
|
"login.password": "密码",
|
||||||
|
"login.passwords_do_not_match": "两次输入的密码不一致",
|
||||||
|
"login.privacy": "隐私政策",
|
||||||
|
"login.sign_up_successful": "注册成功",
|
||||||
|
"login.submit": "提交",
|
||||||
|
"login.terms": "服务条款",
|
||||||
"onboarding.edit_profile": "编辑个人资料",
|
"onboarding.edit_profile": "编辑个人资料",
|
||||||
"onboarding.edit_profile_description": "更改你的姓名、电子邮件和头像",
|
"onboarding.edit_profile_description": "更改你的姓名、电子邮件和头像",
|
||||||
"onboarding.finished_description": "你已完成所有设置步骤。开始享受 Folo 带来的精彩体验吧!",
|
"onboarding.finished_description": "你已完成所有设置步骤。开始享受 Folo 带来的精彩体验吧!",
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,25 @@
|
||||||
"ai.summary_upgrade_view_plans": "查看方案",
|
"ai.summary_upgrade_view_plans": "查看方案",
|
||||||
"entry.pull_up_to_next_entry": "繼續上拉查看下一條內容",
|
"entry.pull_up_to_next_entry": "繼續上拉查看下一條內容",
|
||||||
"entry.release_to_next_entry": "鬆開查看下一條內容",
|
"entry.release_to_next_entry": "鬆開查看下一條內容",
|
||||||
|
"entry_actions.toggle_ai_summary": "切換 AI 總結",
|
||||||
|
"entry_content.no_content": "無內容",
|
||||||
|
"entry_content.no_video_url": "未找到影片連結",
|
||||||
|
"entry_list.zero_unread": "全部已讀",
|
||||||
|
"login.agree_to": "繼續即表示您同意我們的",
|
||||||
"login.back": "返回",
|
"login.back": "返回",
|
||||||
|
"login.confirm_password.label": "確認密碼",
|
||||||
"login.continueWith": "透過 {{provider}} 繼續",
|
"login.continueWith": "透過 {{provider}} 繼續",
|
||||||
|
"login.email": "電子信箱",
|
||||||
|
"login.forget_password.note": "忘記密碼了嗎?",
|
||||||
"login.have_account": "已有帳號?<strong>登入</strong>",
|
"login.have_account": "已有帳號?<strong>登入</strong>",
|
||||||
|
"login.invalid_email_or_password": "電子郵件或密碼無效",
|
||||||
"login.no_account": "沒有帳號?<strong>註冊</strong>",
|
"login.no_account": "沒有帳號?<strong>註冊</strong>",
|
||||||
|
"login.password": "密碼",
|
||||||
|
"login.passwords_do_not_match": "兩次輸入的密碼不一致",
|
||||||
|
"login.privacy": "隱私政策",
|
||||||
|
"login.sign_up_successful": "註冊成功",
|
||||||
|
"login.submit": "送出",
|
||||||
|
"login.terms": "服務條款",
|
||||||
"onboarding.edit_profile": "編輯個人資料",
|
"onboarding.edit_profile": "編輯個人資料",
|
||||||
"onboarding.edit_profile_description": "更改你的姓名、電子郵件和頭像",
|
"onboarding.edit_profile_description": "更改你的姓名、電子郵件和頭像",
|
||||||
"onboarding.finished_description": "你已完成所有設定步驟。開始享受 Folo 帶來的精彩體驗吧!",
|
"onboarding.finished_description": "你已完成所有設定步驟。開始享受 Folo 帶來的精彩體驗吧!",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
{
|
||||||
|
"contextMenu.copy": "Copier",
|
||||||
|
"contextMenu.copyImage": "Copier l'image",
|
||||||
|
"contextMenu.copyImageAddress": "Copier l'adresse de l'image",
|
||||||
|
"contextMenu.copyLink": "Copier le lien",
|
||||||
|
"contextMenu.copyVideoAddress": "Copier l'adresse de la vidéo",
|
||||||
|
"contextMenu.cut": "Couper",
|
||||||
|
"contextMenu.inspect": "Inspecter l'élément",
|
||||||
|
"contextMenu.learnSpelling": "Apprendre l'orthographe",
|
||||||
|
"contextMenu.lookUpSelection": "Rechercher la sélection",
|
||||||
|
"contextMenu.openImageInBrowser": "Ouvrir l'image dans le navigateur",
|
||||||
|
"contextMenu.openLinkInBrowser": "Ouvrir le lien dans le navigateur",
|
||||||
|
"contextMenu.paste": "Coller",
|
||||||
|
"contextMenu.saveImage": "Enregistrer l'image",
|
||||||
|
"contextMenu.saveImageAs": "Enregistrer l'image sous...",
|
||||||
|
"contextMenu.saveLinkAs": "Enregistrer le lien sous...",
|
||||||
|
"contextMenu.saveVideo": "Enregistrer la vidéo",
|
||||||
|
"contextMenu.saveVideoAs": "Enregistrer la vidéo sous...",
|
||||||
|
"contextMenu.searchWithGoogle": "Rechercher avec Google",
|
||||||
|
"contextMenu.selectAll": "Tout sélectionner",
|
||||||
|
"contextMenu.services": "Services",
|
||||||
|
"dialog.cancel": "Annuler",
|
||||||
|
"dialog.clearAllData": "Êtes-vous sûr de vouloir effacer toutes les données ?",
|
||||||
|
"dialog.no": "Non",
|
||||||
|
"dialog.open": "Ouvrir",
|
||||||
|
"dialog.openExternalApp.message": "Êtes-vous sûr de vouloir ouvrir \"{{url}}\" avec une autre application ?",
|
||||||
|
"dialog.openExternalApp.title": "Ouvrir une application externe ?",
|
||||||
|
"dialog.yes": "Oui",
|
||||||
|
"menu.about": "À propos de {{name}}",
|
||||||
|
"menu.actualSize": "Taille réelle",
|
||||||
|
"menu.bringAllToFront": "Tout mettre au premier plan",
|
||||||
|
"menu.checkForUpdates": "Vérifier les mises à jour",
|
||||||
|
"menu.clearAllData": "Effacer toutes les données",
|
||||||
|
"menu.close": "Fermer",
|
||||||
|
"menu.copy": "Copier",
|
||||||
|
"menu.cut": "Couper",
|
||||||
|
"menu.debug": "Déboguer",
|
||||||
|
"menu.delete": "Supprimer",
|
||||||
|
"menu.discover": "Découvrir",
|
||||||
|
"menu.edit": "Édition",
|
||||||
|
"menu.file": "Fichier",
|
||||||
|
"menu.followReleases": "Versions Folo",
|
||||||
|
"menu.forceReload": "Forcer le rechargement",
|
||||||
|
"menu.front": "Mettre au premier plan",
|
||||||
|
"menu.help": "Aide",
|
||||||
|
"menu.hide": "Masquer {{name}}",
|
||||||
|
"menu.hideOthers": "Masquer les autres",
|
||||||
|
"menu.minimize": "Réduire",
|
||||||
|
"menu.open": "Ouvrir {{name}}",
|
||||||
|
"menu.openLogFile": "Ouvrir le fichier de journal",
|
||||||
|
"menu.paste": "Coller",
|
||||||
|
"menu.pasteAndMatchStyle": "Coller et adapter le style",
|
||||||
|
"menu.quickAdd": "Ajout rapide",
|
||||||
|
"menu.quit": "Quitter {{name}}",
|
||||||
|
"menu.quitAndInstallUpdate": "Debug : Quitter et installer la mise à jour",
|
||||||
|
"menu.redo": "Rétablir",
|
||||||
|
"menu.reload": "Recharger",
|
||||||
|
"menu.search": "Rechercher",
|
||||||
|
"menu.selectAll": "Tout sélectionner",
|
||||||
|
"menu.services": "Services",
|
||||||
|
"menu.settings": "Paramètres...",
|
||||||
|
"menu.speech": "Parole",
|
||||||
|
"menu.startSpeaking": "Commencer la lecture",
|
||||||
|
"menu.stopSpeaking": "Arrêter la lecture",
|
||||||
|
"menu.toggleDevTools": "Basculer les outils de développement",
|
||||||
|
"menu.toggleFullScreen": "Basculer en plein écran",
|
||||||
|
"menu.undo": "Annuler",
|
||||||
|
"menu.unread": "Non lu",
|
||||||
|
"menu.view": "Affichage",
|
||||||
|
"menu.window": "Fenêtre",
|
||||||
|
"menu.zoom": "Zoom",
|
||||||
|
"menu.zoomIn": "Zoom avant",
|
||||||
|
"menu.zoomOut": "Zoom arrière"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,803 @@
|
||||||
|
{
|
||||||
|
"about.aiOnboardingDescription": "Revisiter la visite d'intégration interactive.",
|
||||||
|
"about.appTip": "Fonctionnalités de l'appli",
|
||||||
|
"about.appTipDescription": "Introduction aux fonctionnalités de l'appli et guide d'utilisation.",
|
||||||
|
"about.changelog": "Journal des modifications",
|
||||||
|
"about.changelogDescription": "Voir les nouveautés de chaque version",
|
||||||
|
"about.checkForUpdates": "Vérifier les mises à jour",
|
||||||
|
"about.checkNow": "Vérifier maintenant",
|
||||||
|
"about.checkingForUpdates": "Vérification des mises à jour...",
|
||||||
|
"about.copyEnvironment": "Copier l'environnement",
|
||||||
|
"about.environmentCopied": "Infos d'environnement copiées",
|
||||||
|
"about.feedbackInfo": "{{appName}} ({{commitSha}}) est open-source et activement développé sur GitHub. Si vous avez des commentaires ou des suggestions, n'hésitez pas à <OpenIssueLink>ouvrir un ticket</OpenIssueLink> sur notre GitHub.",
|
||||||
|
"about.iconLibrary": "La bibliothèque d'icônes utilisée est protégée par les droits d'auteur de <IconLibraryLink /> et ne peut pas être redistribuée.",
|
||||||
|
"about.legal": "Légal",
|
||||||
|
"about.licenseInfo": "Copyright © {{currentYear}} {{appName}}. Tous droits réservés.",
|
||||||
|
"about.noUpdateAvailable": "Vous avez la dernière version",
|
||||||
|
"about.privacyPolicy": "Politique de confidentialité",
|
||||||
|
"about.projectLicense": "{{appName}} est sous licence GNU General Public License version 3 avec des exceptions supplémentaires.",
|
||||||
|
"about.resources": "Ressources & Contributions",
|
||||||
|
"about.sidebar_title": "À propos",
|
||||||
|
"about.socialMedia": "Réseaux sociaux",
|
||||||
|
"about.termsOfService": "Conditions d'utilisation",
|
||||||
|
"about.updateAvailable": "Mise à jour disponible !",
|
||||||
|
"about.updateCheckFailed": "Échec de la vérification des mises à jour",
|
||||||
|
"about.updateDescription": "Maintenez votre appli à jour avec les dernières fonctionnalités et améliorations",
|
||||||
|
"about.viewChangelog": "Voir le journal des modifications",
|
||||||
|
"actions.actionName": "Action {{number}}",
|
||||||
|
"actions.action_card.add": "Ajouter",
|
||||||
|
"actions.action_card.all": "Tout",
|
||||||
|
"actions.action_card.and": "Et",
|
||||||
|
"actions.action_card.block": "Bloquer",
|
||||||
|
"actions.action_card.block_rules": "Règles de blocage",
|
||||||
|
"actions.action_card.custom_filters": "Filtres personnalisés",
|
||||||
|
"actions.action_card.empty.description": "Créez votre première règle d'action pour traiter automatiquement vos flux.",
|
||||||
|
"actions.action_card.empty.start": "Commencez ici !",
|
||||||
|
"actions.action_card.empty.title": "Aucune action pour le moment",
|
||||||
|
"actions.action_card.enable_readability": "Activer la lisibilité",
|
||||||
|
"actions.action_card.feed_options.entry_attachments_duration": "Durée de la vidéo",
|
||||||
|
"actions.action_card.feed_options.entry_author": "Auteur",
|
||||||
|
"actions.action_card.feed_options.entry_content": "Contenu",
|
||||||
|
"actions.action_card.feed_options.entry_media_length": "Longueur du média",
|
||||||
|
"actions.action_card.feed_options.entry_title": "Titre",
|
||||||
|
"actions.action_card.feed_options.entry_url": "URL",
|
||||||
|
"actions.action_card.feed_options.feed_category": "Catégorie du flux",
|
||||||
|
"actions.action_card.feed_options.feed_title": "Titre du flux",
|
||||||
|
"actions.action_card.feed_options.feed_url": "URL du flux",
|
||||||
|
"actions.action_card.feed_options.site_url": "URL du site",
|
||||||
|
"actions.action_card.feed_options.status": "Statut",
|
||||||
|
"actions.action_card.feed_options.subscription_view": "Vue d'abonnement",
|
||||||
|
"actions.action_card.field": "Champ",
|
||||||
|
"actions.action_card.from": "De",
|
||||||
|
"actions.action_card.generate_summary": "Générer un résumé avec l'IA",
|
||||||
|
"actions.action_card.name": "Nom",
|
||||||
|
"actions.action_card.new_entry_notification": "Notification de nouvelle entrée",
|
||||||
|
"actions.action_card.no_translation": "Pas de traduction",
|
||||||
|
"actions.action_card.operation_options.contains": "Contient",
|
||||||
|
"actions.action_card.operation_options.does_not_contain": "Ne contient pas",
|
||||||
|
"actions.action_card.operation_options.is_equal_to": "Est égal à",
|
||||||
|
"actions.action_card.operation_options.is_greater_than": "Est plus grand que",
|
||||||
|
"actions.action_card.operation_options.is_less_than": "Est plus petit que",
|
||||||
|
"actions.action_card.operation_options.is_not_equal_to": "N'est pas égal à",
|
||||||
|
"actions.action_card.operation_options.matches_regex": "Correspond à l'expression régulière",
|
||||||
|
"actions.action_card.operator": "Opérateur",
|
||||||
|
"actions.action_card.or": "Ou",
|
||||||
|
"actions.action_card.rewrite_rules": "Règles de réécriture",
|
||||||
|
"actions.action_card.settings": "Paramètres",
|
||||||
|
"actions.action_card.silence": "Silence",
|
||||||
|
"actions.action_card.source_content": "Voir le contenu source",
|
||||||
|
"actions.action_card.star": "Favori",
|
||||||
|
"actions.action_card.summary.action_count": "{{count}} actions activées",
|
||||||
|
"actions.action_card.summary.active": "Actif",
|
||||||
|
"actions.action_card.summary.copy": "Copier dans le presse-papier",
|
||||||
|
"actions.action_card.summary.delete": "Supprimer",
|
||||||
|
"actions.action_card.summary.delete_message": "Êtes-vous sûr de vouloir supprimer cette règle ? Cette action est irréversible.",
|
||||||
|
"actions.action_card.summary.delete_title": "Supprimer la règle",
|
||||||
|
"actions.action_card.summary.disabled": "Désactivé",
|
||||||
|
"actions.action_card.summary.empty": "Aucune règle pour le moment",
|
||||||
|
"actions.action_card.summary.export": "Exporter vers un fichier",
|
||||||
|
"actions.action_card.summary.helper": "Automatisez votre flux de travail avec des déclencheurs et des actions précis.",
|
||||||
|
"actions.action_card.summary.import": "Importer",
|
||||||
|
"actions.action_card.summary.import_clipboard": "Importer depuis le presse-papier",
|
||||||
|
"actions.action_card.summary.import_file": "Importer depuis un fichier",
|
||||||
|
"actions.action_card.summary.no_actions": "Aucune action configurée pour le moment",
|
||||||
|
"actions.action_card.summary.rule_count": "{{count}} règles",
|
||||||
|
"actions.action_card.summary.share": "Partager",
|
||||||
|
"actions.action_card.summary.toggle": "Basculer le statut de la règle",
|
||||||
|
"actions.action_card.then_do": "Alors faire...",
|
||||||
|
"actions.action_card.to": "À",
|
||||||
|
"actions.action_card.translate_into": "Traduire",
|
||||||
|
"actions.action_card.value": "Valeur",
|
||||||
|
"actions.action_card.webhooks": "Webhooks",
|
||||||
|
"actions.action_card.when_feeds_match": "Quand les flux correspondent...",
|
||||||
|
"actions.condition": "Condition",
|
||||||
|
"actions.conditions": "Conditions",
|
||||||
|
"actions.edit_condition": "Modifier la condition",
|
||||||
|
"actions.edit_rewrite_rule": "Modifier la règle de réécriture",
|
||||||
|
"actions.edit_rule": "Modifier la règle",
|
||||||
|
"actions.edit_webhook": "Modifier le webhook",
|
||||||
|
"actions.info": "Les actions sont des collections de règles que vous pouvez automatiser pour effectuer des tâches côté serveur ou client.",
|
||||||
|
"actions.navigate.prompt": "Vous avez des modifications d'action non enregistrées. Êtes-vous sûr de vouloir quitter ?",
|
||||||
|
"actions.newRule": "Nouvelle règle",
|
||||||
|
"actions.save": "Enregistrer",
|
||||||
|
"actions.saveSuccess": "🎉 Actions enregistrées.",
|
||||||
|
"actions.sidebar_title": "Actions",
|
||||||
|
"actions.title": "Actions",
|
||||||
|
"ai.personalize.prompt.description": "Parlez de vous à Folo et de votre façon de lire.",
|
||||||
|
"ai.personalize.prompt.label": "Invite de personnalisation",
|
||||||
|
"ai.personalize.title": "Personnaliser",
|
||||||
|
"ai.shortcuts.title": "Raccourcis",
|
||||||
|
"appearance.accent_color.description": "Choisissez la couleur d'accentuation pour l'interface de l'application.",
|
||||||
|
"appearance.accent_color.label": "Couleur d'accentuation",
|
||||||
|
"appearance.code_highlight_theme.description": "Ajustez le thème de mise en évidence du code.",
|
||||||
|
"appearance.code_highlight_theme.label": "Thème du code",
|
||||||
|
"appearance.code_highlighting.title": "Mise en évidence du code",
|
||||||
|
"appearance.common.title": "Commun",
|
||||||
|
"appearance.content": "Contenu",
|
||||||
|
"appearance.content_display.title": "Affichage du contenu",
|
||||||
|
"appearance.content_font.default": "Défaut (Police UI)",
|
||||||
|
"appearance.content_font.description": "Ajustez la police utilisée pour le contenu de lecture.",
|
||||||
|
"appearance.content_font.label": "Police du contenu",
|
||||||
|
"appearance.content_font_size": "Taille de police du contenu",
|
||||||
|
"appearance.content_line_height.description": "Ajustez l'espacement entre les lignes de texte dans les articles.",
|
||||||
|
"appearance.content_line_height.label": "Hauteur de ligne",
|
||||||
|
"appearance.content_line_height.loose": "Lâche",
|
||||||
|
"appearance.content_line_height.normal": "Normal",
|
||||||
|
"appearance.content_line_height.relaxed": "Relaxé",
|
||||||
|
"appearance.content_line_height.snug": "Serré",
|
||||||
|
"appearance.content_line_height.tight": "Étroit",
|
||||||
|
"appearance.custom_css.button": "Modifier",
|
||||||
|
"appearance.custom_css.description": "Style CSS personnalisé pour le contenu.",
|
||||||
|
"appearance.custom_css.label": "CSS personnalisé",
|
||||||
|
"appearance.custom_font": "Police personnalisée",
|
||||||
|
"appearance.customization.title": "Personnalisation",
|
||||||
|
"appearance.customize_sub_tabs.description": "Personnalisez les onglets d'abonnement à votre goût.",
|
||||||
|
"appearance.customize_sub_tabs.label": "Personnaliser les onglets",
|
||||||
|
"appearance.customize_toolbar.description": "Personnalisez la barre d'outils de contenu à votre goût.",
|
||||||
|
"appearance.customize_toolbar.label": "Personnaliser la barre d'outils",
|
||||||
|
"appearance.date_format.description": "Ajustez le format d'affichage de la date.",
|
||||||
|
"appearance.date_format.label": "Format de date",
|
||||||
|
"appearance.font.custom": "Personnalisé",
|
||||||
|
"appearance.font.system": "Système UI",
|
||||||
|
"appearance.font_scaling.content_different.description": "Définir une taille de police indépendante pour le contenu des articles.",
|
||||||
|
"appearance.font_scaling.content_different.label": "Utiliser une taille différente pour le contenu",
|
||||||
|
"appearance.font_scaling.content_size.description": "Définir la taille de police pour le contenu des articles.",
|
||||||
|
"appearance.font_scaling.content_size.l": "Grand",
|
||||||
|
"appearance.font_scaling.content_size.label": "Taille de police du contenu",
|
||||||
|
"appearance.font_scaling.content_size.m": "Défaut",
|
||||||
|
"appearance.font_scaling.content_size.s": "Petit",
|
||||||
|
"appearance.font_scaling.content_size.xl": "Plus grand",
|
||||||
|
"appearance.font_scaling.content_size.xs": "Plus petit",
|
||||||
|
"appearance.font_scaling.scale.description": "Ajustez le facteur d'échelle de la taille de police.",
|
||||||
|
"appearance.font_scaling.scale.label": "Échelle de police",
|
||||||
|
"appearance.font_scaling.size.l": "Grand",
|
||||||
|
"appearance.font_scaling.size.m": "Défaut",
|
||||||
|
"appearance.font_scaling.size.s": "Petit",
|
||||||
|
"appearance.font_scaling.size.xl": "Plus grand",
|
||||||
|
"appearance.font_scaling.size.xs": "Plus petit",
|
||||||
|
"appearance.font_scaling.system.description": "Suivre les paramètres de taille de police d'accessibilité du système.",
|
||||||
|
"appearance.font_scaling.system.label": "Utiliser l'échelle système",
|
||||||
|
"appearance.font_scaling.title": "Mise à l'échelle de la police",
|
||||||
|
"appearance.fonts": "Polices",
|
||||||
|
"appearance.general": "Général",
|
||||||
|
"appearance.global_font.default": "Suivre le système",
|
||||||
|
"appearance.global_font_size.description": "Ajustez la taille globale du texte.",
|
||||||
|
"appearance.global_font_size.label": "Taille de police globale",
|
||||||
|
"appearance.guess_code_language.description": "Principaux langages de programmation qui utilisent des modèles pour déduire les blocs de code non étiquetés.",
|
||||||
|
"appearance.guess_code_language.label": "Deviner le langage du code",
|
||||||
|
"appearance.hide_extra_badge.description": "Masquer les badges spéciaux du flux dans la barre latérale, ex. Boost, Réclamé.",
|
||||||
|
"appearance.hide_extra_badge.label": "Masquer les badges spéciaux",
|
||||||
|
"appearance.hide_recent_reader.description": "Masquer les lecteurs récents et le nombre de vues dans l'entrée.",
|
||||||
|
"appearance.hide_recent_reader.label": "Masquer lecteurs récents & vues",
|
||||||
|
"appearance.interface_window.title": "Interface & Fenêtre",
|
||||||
|
"appearance.misc": "Divers",
|
||||||
|
"appearance.modal_overlay.description": "Afficher la superposition modale.",
|
||||||
|
"appearance.modal_overlay.label": "Afficher la superposition modale",
|
||||||
|
"appearance.opaque_sidebars.description": "Rendre l'arrière-plan de la barre latérale transparent.",
|
||||||
|
"appearance.opaque_sidebars.label": "Barres latérales opaques",
|
||||||
|
"appearance.reader_render_inline_style.description": "Permet le rendu du style en ligne du HTML original.",
|
||||||
|
"appearance.reader_render_inline_style.label": "Rendre le style en ligne",
|
||||||
|
"appearance.reading_view.title": "Vue de lecture",
|
||||||
|
"appearance.reduce_motion.description": "Réduire le mouvement des éléments pour améliorer les performances et réduire la consommation d'énergie.",
|
||||||
|
"appearance.reduce_motion.label": "Réduire le mouvement",
|
||||||
|
"appearance.save": "Enregistrer",
|
||||||
|
"appearance.sidebar": "Barre latérale",
|
||||||
|
"appearance.sidebar_title": "Apparence",
|
||||||
|
"appearance.subscription_list.title": "Liste d'abonnements",
|
||||||
|
"appearance.subscriptions": "Abonnements",
|
||||||
|
"appearance.system_integration.title": "Intégration système",
|
||||||
|
"appearance.text_size.default": "Défaut",
|
||||||
|
"appearance.text_size.label": "Taille du texte global",
|
||||||
|
"appearance.text_size.large": "Grand",
|
||||||
|
"appearance.text_size.medium": "Moyen",
|
||||||
|
"appearance.text_size.smaller": "Plus petit",
|
||||||
|
"appearance.theme.dark": "Sombre",
|
||||||
|
"appearance.theme.description": "Ajustez le thème global de l'application.",
|
||||||
|
"appearance.theme.label": "Thème",
|
||||||
|
"appearance.theme.light": "Clair",
|
||||||
|
"appearance.theme.system": "Système",
|
||||||
|
"appearance.thumbnail_ratio.description": "Le ratio de la vignette dans la liste des entrées.",
|
||||||
|
"appearance.thumbnail_ratio.original": "Original",
|
||||||
|
"appearance.thumbnail_ratio.square": "Carré",
|
||||||
|
"appearance.thumbnail_ratio.title": "Ratio de vignette",
|
||||||
|
"appearance.title": "Apparence",
|
||||||
|
"appearance.typography.title": "Typographie",
|
||||||
|
"appearance.ui_font.description": "Ajustez la police utilisée pour les éléments de l'interface utilisateur.",
|
||||||
|
"appearance.ui_font.label": "Police globale",
|
||||||
|
"appearance.unread_count.badge.description": "Afficher le nombre de non lus comme un badge dans l'icône du dock.",
|
||||||
|
"appearance.unread_count.badge.label": "Afficher comme badge",
|
||||||
|
"appearance.unread_count.label": "Nombre de non lus",
|
||||||
|
"appearance.unread_count.sidebar.description": "Afficher le nombre de non lus à côté des flux et des groupes.",
|
||||||
|
"appearance.unread_count.sidebar.title": "Afficher le nombre de non lus",
|
||||||
|
"appearance.unread_count.view_and_subscription.description": "Afficher le nombre de non lus dans la vue et la liste d'abonnement.",
|
||||||
|
"appearance.unread_count.view_and_subscription.label": "Afficher dans Vue et Abonnement",
|
||||||
|
"appearance.use_pointer_cursor.description": "Lorsque la souris survole un élément interactif, le curseur apparaît comme une main.",
|
||||||
|
"appearance.use_pointer_cursor.label": "Utiliser le curseur main",
|
||||||
|
"appearance.words.customize": "Personnaliser",
|
||||||
|
"common.give_star": "<HeartIcon />Vous aimez notre produit ? <Link>Donnez-nous une étoile sur GitHub !</Link>",
|
||||||
|
"control.paid_badge.basic_or_higher": "Cette fonctionnalité nécessite un plan Basique ou supérieur",
|
||||||
|
"control.paid_badge.free_limited": "Cette fonctionnalité est limitée pour le plan gratuit",
|
||||||
|
"customizeToolbar.more_actions.description": "Sera affiché dans le menu déroulant.",
|
||||||
|
"customizeToolbar.more_actions.title": "Plus d'actions",
|
||||||
|
"customizeToolbar.quick_actions.description": "Personnalisez et réorganisez vos actions fréquemment utilisées.",
|
||||||
|
"customizeToolbar.quick_actions.title": "Actions rapides",
|
||||||
|
"customizeToolbar.reset_layout": "Réinitialiser la disposition par défaut",
|
||||||
|
"customizeToolbar.title": "Personnaliser la barre d'outils",
|
||||||
|
"data_control.app_cache_limit.description": "La taille maximale du cache de l'application. Une fois le cache atteint cette taille, les éléments les plus anciens seront supprimés pour libérer de l'espace.",
|
||||||
|
"data_control.app_cache_limit.label": "Limite de cache de l'application",
|
||||||
|
"data_control.clean_cache.button": "Nettoyer le cache",
|
||||||
|
"data_control.clean_cache.cancel": "Annuler",
|
||||||
|
"data_control.clean_cache.clear": "Effacer",
|
||||||
|
"data_control.clean_cache.description": "Nettoyez le cache de l'application pour libérer de l'espace.",
|
||||||
|
"data_control.clean_cache.description_web": "Nettoyez le cache du service worker de l'application web pour libérer de l'espace.",
|
||||||
|
"data_control.clean_cache.success": "Cache nettoyé avec succès.",
|
||||||
|
"data_control.data_sources": "Sources de données",
|
||||||
|
"data_control.export_local_database.label": "Exporter la base de données locale",
|
||||||
|
"data_control.import_opml.label": "Importer des abonnements depuis OPML",
|
||||||
|
"data_control.utils": "Utilitaires",
|
||||||
|
"discoverFilters.filters": "Filtres",
|
||||||
|
"discoverFilters.language": "Langue",
|
||||||
|
"discoverFilters.title": "Filtres de découverte",
|
||||||
|
"feeds.claim": "Réclamer des flux",
|
||||||
|
"feeds.claimTips": "Pour réclamer vos flux et recevoir des pourboires, faites un clic droit sur le flux dans votre liste d'abonnement et sélectionnez Réclamer.",
|
||||||
|
"feeds.filter.all": "Tout ({{count}})",
|
||||||
|
"feeds.filter.rsshub": "RSSHub ({{count}})",
|
||||||
|
"feeds.noFeeds": "Aucun flux réclamé",
|
||||||
|
"feeds.subscription": "Flux abonnés",
|
||||||
|
"feeds.tableHeaders.date": "Date d'abonnement",
|
||||||
|
"feeds.tableHeaders.followers": "Abonnés",
|
||||||
|
"feeds.tableHeaders.name": "Nom",
|
||||||
|
"feeds.tableHeaders.subscriptionCount": "Abos",
|
||||||
|
"feeds.tableHeaders.tipAmount": "Pourboires",
|
||||||
|
"feeds.tableHeaders.updatesPerWeek": "Mises à jour",
|
||||||
|
"feeds.tableHeaders.view": "Vue",
|
||||||
|
"feeds.tableSelected.clear": "Effacer",
|
||||||
|
"feeds.tableSelected.item": "{{count}} élément(s) sélectionné(s)",
|
||||||
|
"feeds.tableSelected.moveToView.action": "Déplacer vers la vue",
|
||||||
|
"feeds.tableSelected.moveToView.confirm": "Êtes-vous sûr de vouloir déplacer ces flux vers {{view}} ?",
|
||||||
|
"feeds.tableSelected.moveToView.confirmTitle": "Confirmer",
|
||||||
|
"feeds.tableSelected.unsubscribe": "Se désabonner",
|
||||||
|
"general.action.summary.description": "Générer un résumé de l'entrée en utilisant l'IA.",
|
||||||
|
"general.action.summary.label": "Résumé IA",
|
||||||
|
"general.action.title": "Actions IA",
|
||||||
|
"general.action.translation.description": "Traduire l'entrée dans la langue sélectionnée.",
|
||||||
|
"general.action.translation.label": "Traduction IA",
|
||||||
|
"general.action_language.default": "Défaut (Langue UI)",
|
||||||
|
"general.action_language.description": "Choisissez la langue pour les actions IA, ex. Résumé IA, Traduction IA.",
|
||||||
|
"general.action_language.label": "Langue de sortie IA",
|
||||||
|
"general.advanced": "Avancé",
|
||||||
|
"general.app": "Appli",
|
||||||
|
"general.auto_expand_long_social_media.description": "Développer automatiquement les entrées de réseaux sociaux contenant de longs textes.",
|
||||||
|
"general.auto_expand_long_social_media.label": "Développer longs réseaux sociaux",
|
||||||
|
"general.auto_group.description": "Grouper les flux du même domaine de site web ensemble.",
|
||||||
|
"general.auto_group.label": "Grouper auto. les flux par site",
|
||||||
|
"general.cache": "Cache",
|
||||||
|
"general.content": "Contenu",
|
||||||
|
"general.data": "Données",
|
||||||
|
"general.data_file.label": "Fichier de données",
|
||||||
|
"general.dim_read.description": "Atténuer la couleur des entrées lues dans la chronologie.",
|
||||||
|
"general.dim_read.label": "Estomper les éléments lus",
|
||||||
|
"general.enhanced.description": "L'activation des paramètres améliorés offre plus d'options de personnalisation, mais peut également introduire des problèmes imprévus. !!! Gardez cela comme porte d'entrée vers des choses vraiment expérimentales/complexes si nécessaire plus tard, sinon cela pourrait être supprimable si d'autres paramètres couvrent les spécificités.",
|
||||||
|
"general.enhanced.disabled.tip": "Les paramètres améliorés sont désactivés, vous pouvez les activer dans Paramètres généraux - Avancé.",
|
||||||
|
"general.enhanced.enable.modal.cancel": "Annuler",
|
||||||
|
"general.enhanced.enable.modal.confirm": "Activer",
|
||||||
|
"general.enhanced.enable.modal.description": "L'activation des paramètres améliorés offre plus d'options de personnalisation, mais peut également introduire des problèmes imprévus. N'activez pas cela à moins de savoir ce que vous faites.",
|
||||||
|
"general.enhanced.enable.modal.title": "Activer les paramètres améliorés",
|
||||||
|
"general.enhanced.enabled.tip": "Les paramètres améliorés sont activés, vous pouvez les désactiver dans Paramètres généraux - Avancé.",
|
||||||
|
"general.enhanced.label": "Paramètres améliorés",
|
||||||
|
"general.export.button": "Exporter",
|
||||||
|
"general.export.description": "Exporter votre liste d'abonnements aux flux.",
|
||||||
|
"general.export.folder_mode.description": "Décidez comment vous souhaitez organiser vos dossiers d'exportation.",
|
||||||
|
"general.export.folder_mode.label": "Mode dossier",
|
||||||
|
"general.export.folder_mode.option.category": "Catégorie",
|
||||||
|
"general.export.folder_mode.option.view": "Vue",
|
||||||
|
"general.export.label": "Exporter les flux (OPML)",
|
||||||
|
"general.export.rsshub_url.description": "URL de base par défaut pour la route RSSHub, laissez vide pour utiliser https://rsshub.app.",
|
||||||
|
"general.export.rsshub_url.label": "URL RSSHub",
|
||||||
|
"general.export_data.title": "Exporter les données",
|
||||||
|
"general.export_database.button": "Exporter",
|
||||||
|
"general.export_database.description": "Exporter toutes vos données, y compris les articles (Sauvegarde complète).",
|
||||||
|
"general.export_database.label": "Exporter la base de données",
|
||||||
|
"general.group_by_date.description": "Grouper les entrées par date.",
|
||||||
|
"general.group_by_date.label": "Grouper par date",
|
||||||
|
"general.hide_all_read_subscriptions.description": "Masquer les abonnements sans entrées non lues dans la liste d'abonnements.",
|
||||||
|
"general.hide_all_read_subscriptions.label": "Masquer les lus",
|
||||||
|
"general.hide_private_subscriptions_in_timeline.description": "Masquer les abonnements privés de votre liste d'abonnements et masquer leurs entrées de votre chronologie (ils sont toujours invisibles au public indépendamment de ce paramètre).",
|
||||||
|
"general.hide_private_subscriptions_in_timeline.label": "Masquer les privés",
|
||||||
|
"general.language.description": "Choisissez la langue d'affichage de l'application.",
|
||||||
|
"general.language.title": "Langue",
|
||||||
|
"general.launch_at_login": "Lancer à la connexion",
|
||||||
|
"general.log_file.button": "Révéler",
|
||||||
|
"general.log_file.description": "Révéler le fichier journal dans le système.",
|
||||||
|
"general.log_file.label": "Fichier journal",
|
||||||
|
"general.maintenance.title": "Maintenance",
|
||||||
|
"general.mark_as_read.hover.description": "Marquer automatiquement les entrées comme lues au survol.",
|
||||||
|
"general.mark_as_read.hover.label": "Au survol de l'article",
|
||||||
|
"general.mark_as_read.render.description": "Marquer les éléments comme les publications sociales ou les images comme lus immédiatement.",
|
||||||
|
"general.mark_as_read.render.label": "Éléments simples lorsqu'ils entrent dans la vue",
|
||||||
|
"general.mark_as_read.scroll.description": "Marquer automatiquement les entrées comme lues lorsqu'elles défilent hors de la vue.",
|
||||||
|
"general.mark_as_read.scroll.label": "En faisant défiler l'article",
|
||||||
|
"general.mark_as_read.title": "Marquer comme lu",
|
||||||
|
"general.minimize_to_tray.description": "Minimiser dans la barre d'état système lors de la fermeture de la fenêtre.",
|
||||||
|
"general.minimize_to_tray.label": "Minimiser dans la barre d'état",
|
||||||
|
"general.network": "Réseau",
|
||||||
|
"general.open_links_in_external_app.label": "Ouvrir les liens dans une appli externe",
|
||||||
|
"general.privacy": "Confidentialité",
|
||||||
|
"general.proxy.description": "Définir le proxy pour le routage du trafic réseau, ex. socks://proxy.example.com:1080.",
|
||||||
|
"general.proxy.label": "Proxy",
|
||||||
|
"general.rebuild_database.button": "Reconstruire",
|
||||||
|
"general.rebuild_database.cancel": "Annuler",
|
||||||
|
"general.rebuild_database.description": "Si vous rencontrez des problèmes de rendu, la reconstruction de la base de données peut les résoudre.",
|
||||||
|
"general.rebuild_database.label": "Reconstruire la base de données",
|
||||||
|
"general.rebuild_database.title": "Reconstruire la base de données",
|
||||||
|
"general.rebuild_database.warning.line1": "La reconstruction de la base de données effacera toutes vos données locales.",
|
||||||
|
"general.rebuild_database.warning.line2": "Êtes-vous sûr de vouloir continuer ?",
|
||||||
|
"general.send_anonymous_data.description": "En choisissant d'envoyer des données de télémétrie anonymisées, vous contribuez à améliorer l'expérience utilisateur globale de Folo.",
|
||||||
|
"general.send_anonymous_data.label": "Envoyer des données anonymes",
|
||||||
|
"general.show_quick_timeline.description": "Afficher la chronologie rapide en haut de la liste des flux.",
|
||||||
|
"general.show_quick_timeline.label": "Afficher la chronologie de la liste des flux",
|
||||||
|
"general.show_unread_on_launch.description": "Filtrer automatiquement sur le contenu non lu au démarrage de l'application.",
|
||||||
|
"general.show_unread_on_launch.label": "Non lu uniquement au démarrage",
|
||||||
|
"general.sidebar_title": "Général",
|
||||||
|
"general.subscription": "Abonnement",
|
||||||
|
"general.subscriptions": "Abonnements",
|
||||||
|
"general.timeline": "Chronologie",
|
||||||
|
"general.translation_mode.bilingual": "Comparaison bilingue",
|
||||||
|
"general.translation_mode.description": "Choisissez comment le texte traduit est affiché dans la liste des entrées.",
|
||||||
|
"general.translation_mode.label": "Mode de traduction IA",
|
||||||
|
"general.translation_mode.translation-only": "Seulement la traduction",
|
||||||
|
"general.voices": "Voix",
|
||||||
|
"integration.builtin.title": "Intégration intégrée",
|
||||||
|
"integration.categories.custom_integrations": "Actions personnalisées",
|
||||||
|
"integration.categories.download_tools": "Outils de téléchargement",
|
||||||
|
"integration.categories.knowledge_management": "Gestion des connaissances",
|
||||||
|
"integration.categories.media_tools": "Outils multimédias",
|
||||||
|
"integration.categories.reading_services": "Services de lecture",
|
||||||
|
"integration.cubox.autoMemo.description": "Utiliser automatiquement le mode Mémo lorsque du texte est sélectionné pour enregistrer dans Cubox.",
|
||||||
|
"integration.cubox.autoMemo.label": "Mode Mémo Auto",
|
||||||
|
"integration.cubox.enable.description": "Afficher le bouton 'Enregistrer dans Cubox' si disponible.",
|
||||||
|
"integration.cubox.enable.label": "Activer",
|
||||||
|
"integration.cubox.title": "Cubox",
|
||||||
|
"integration.cubox.token.description": "Veuillez entrer l'URL complète de l'API Cubox, format : https://cubox.pro/c/api/save/xxxxxxxxx. Vous pouvez l'obtenir ici :",
|
||||||
|
"integration.cubox.token.label": "URL API Cubox",
|
||||||
|
"integration.custom_integrations.actions.delete": "Supprimer l'intégration",
|
||||||
|
"integration.custom_integrations.actions.disable": "Désactiver l'intégration",
|
||||||
|
"integration.custom_integrations.actions.edit": "Modifier l'intégration",
|
||||||
|
"integration.custom_integrations.actions.enable": "Activer l'intégration",
|
||||||
|
"integration.custom_integrations.add.button": "Ajouter une nouvelle intégration",
|
||||||
|
"integration.custom_integrations.create.error": "Échec de la création de l'intégration personnalisée",
|
||||||
|
"integration.custom_integrations.create.success": "Intégration personnalisée créée avec succès",
|
||||||
|
"integration.custom_integrations.create.title": "Créer une intégration personnalisée",
|
||||||
|
"integration.custom_integrations.delete.success": "Intégration personnalisée supprimée avec succès",
|
||||||
|
"integration.custom_integrations.edit.error": "Échec de la mise à jour de l'intégration personnalisée",
|
||||||
|
"integration.custom_integrations.edit.success": "Intégration personnalisée mise à jour avec succès",
|
||||||
|
"integration.custom_integrations.edit.title": "Modifier l'intégration personnalisée",
|
||||||
|
"integration.custom_integrations.enable.description": "Autoriser la création d'intégrations de partage personnalisées avec des modèles de récupération prenant en charge diverses méthodes et configurations HTTP.",
|
||||||
|
"integration.custom_integrations.enable.label": "Activer les intégrations personnalisées",
|
||||||
|
"integration.custom_integrations.form.body.description": "Corps de la requête pour les méthodes POST/PUT/PATCH. Prend en charge les espaces réservés et le format JSON.",
|
||||||
|
"integration.custom_integrations.form.body.label": "Corps de la requête",
|
||||||
|
"integration.custom_integrations.form.body.placeholder": "{\"title\": \"[title]\", \"url\": \"[url]\", \"content\": \"[content_markdown]\"}",
|
||||||
|
"integration.custom_integrations.form.fetch_template.label": "Modèle de récupération",
|
||||||
|
"integration.custom_integrations.form.headers.add": "Ajouter un en-tête",
|
||||||
|
"integration.custom_integrations.form.headers.description": "Ajouter des en-têtes personnalisés sous forme de paires clé-valeur. Les valeurs prennent en charge les espaces réservés",
|
||||||
|
"integration.custom_integrations.form.headers.key_placeholder": "Nom de l'en-tête",
|
||||||
|
"integration.custom_integrations.form.headers.label": "En-têtes",
|
||||||
|
"integration.custom_integrations.form.headers.value_placeholder": "Valeur de l'en-tête",
|
||||||
|
"integration.custom_integrations.form.icon.description": "Choisissez une icône pour représenter cette intégration.",
|
||||||
|
"integration.custom_integrations.form.icon.label": "Icône",
|
||||||
|
"integration.custom_integrations.form.method.description": "Sélectionnez la méthode HTTP pour la requête.",
|
||||||
|
"integration.custom_integrations.form.method.label": "Méthode HTTP",
|
||||||
|
"integration.custom_integrations.form.name.label": "Nom de l'intégration",
|
||||||
|
"integration.custom_integrations.form.name.placeholder": "Entrez le nom de l'intégration",
|
||||||
|
"integration.custom_integrations.form.scheme.description": "Entrez le schéma d'URL pour l'application externe. Utilisez des espaces réservés comme [title], [url], [content_markdown], etc.",
|
||||||
|
"integration.custom_integrations.form.scheme.examples.title": "Exemples courants",
|
||||||
|
"integration.custom_integrations.form.scheme.label": "Schéma d'URL",
|
||||||
|
"integration.custom_integrations.form.scheme.placeholder": "ex., obsidian://new?vault=MonCoffre&name=[title]&content=[content_markdown]",
|
||||||
|
"integration.custom_integrations.form.type.description": "Choisissez entre des requêtes API HTTP ou des redirections de schéma d'URL vers des applications externes.",
|
||||||
|
"integration.custom_integrations.form.type.http": "Requête HTTP",
|
||||||
|
"integration.custom_integrations.form.type.label": "Type d'intégration",
|
||||||
|
"integration.custom_integrations.form.type.url_scheme": "Schéma d'URL",
|
||||||
|
"integration.custom_integrations.form.url.description": "Utilisez des espaces réservés : [title], [url], [content_html], [summary], [content_markdown].",
|
||||||
|
"integration.custom_integrations.form.url.label": "URL",
|
||||||
|
"integration.custom_integrations.form.url.placeholder": "https://example.com/api/share",
|
||||||
|
"integration.custom_integrations.icons.bookmark": "Signet",
|
||||||
|
"integration.custom_integrations.icons.document": "Document",
|
||||||
|
"integration.custom_integrations.icons.download": "Télécharger",
|
||||||
|
"integration.custom_integrations.icons.external_link": "Lien externe",
|
||||||
|
"integration.custom_integrations.icons.link": "Lien",
|
||||||
|
"integration.custom_integrations.icons.picture": "Image",
|
||||||
|
"integration.custom_integrations.icons.save": "Enregistrer",
|
||||||
|
"integration.custom_integrations.icons.send": "Envoyer",
|
||||||
|
"integration.custom_integrations.icons.share": "Partager",
|
||||||
|
"integration.custom_integrations.icons.star": "Étoile",
|
||||||
|
"integration.custom_integrations.list.empty.button": "Créer la première intégration",
|
||||||
|
"integration.custom_integrations.list.empty.description": "Créez des intégrations de partage personnalisées avec des modèles de récupération pour intégrer n'importe quel service utilisant des requêtes HTTP",
|
||||||
|
"integration.custom_integrations.list.empty.title": "Aucune intégration personnalisée pour le moment",
|
||||||
|
"integration.custom_integrations.list.title": "Intégrations personnalisées",
|
||||||
|
"integration.custom_integrations.modal.description": "Créez des intégrations de partage personnalisées en utilisant des modèles de récupération avec des méthodes HTTP, des URL, des en-têtes et un corps. Utilisez des espaces réservés comme [title], [url], [content_html], [summary] et [content_markdown].",
|
||||||
|
"integration.custom_integrations.placeholders.click_to_copy": "Cliquez pour copier",
|
||||||
|
"integration.custom_integrations.placeholders.description": "Cliquez sur n'importe quel espace réservé pour le copier dans votre presse-papier.",
|
||||||
|
"integration.custom_integrations.placeholders.help": "Espaces réservés disponibles",
|
||||||
|
"integration.custom_integrations.preview.body": "Corps de la requête",
|
||||||
|
"integration.custom_integrations.preview.failed": "Échec de la génération de l'aperçu",
|
||||||
|
"integration.custom_integrations.preview.generating": "Génération de l'aperçu...",
|
||||||
|
"integration.custom_integrations.preview.headers": "En-têtes",
|
||||||
|
"integration.custom_integrations.preview.placeholders": "Espaces réservés disponibles",
|
||||||
|
"integration.custom_integrations.preview.request": "Requête",
|
||||||
|
"integration.custom_integrations.preview.title": "Aperçu de la requête",
|
||||||
|
"integration.custom_integrations.status.disabled": "Désactivé",
|
||||||
|
"integration.custom_integrations.title": "Intégrations personnalisées",
|
||||||
|
"integration.custom_integrations.validation.invalid": "Le modèle contient des erreurs",
|
||||||
|
"integration.custom_integrations.validation.valid": "Le modèle est valide",
|
||||||
|
"integration.eagle.enable.description": "Afficher le bouton 'Enregistrer le média dans Eagle' si disponible.",
|
||||||
|
"integration.eagle.enable.label": "Activer",
|
||||||
|
"integration.eagle.title": "Eagle",
|
||||||
|
"integration.export.button": "Exporter les paramètres",
|
||||||
|
"integration.export.error": "Échec de l'exportation des paramètres d'intégration",
|
||||||
|
"integration.export.success": "Paramètres d'intégration exportés avec succès",
|
||||||
|
"integration.general": "Général",
|
||||||
|
"integration.import.button": "Importer les paramètres",
|
||||||
|
"integration.import.error": "Échec de l'importation des paramètres d'intégration",
|
||||||
|
"integration.import.invalid": "Fichier de paramètres d'intégration invalide",
|
||||||
|
"integration.import.success": "Paramètres d'intégration importés avec succès",
|
||||||
|
"integration.instapaper.enable.description": "Afficher le bouton 'Enregistrer dans Instapaper' si disponible.",
|
||||||
|
"integration.instapaper.enable.label": "Activer",
|
||||||
|
"integration.instapaper.password.label": "Mot de passe Instapaper",
|
||||||
|
"integration.instapaper.title": "Instapaper",
|
||||||
|
"integration.instapaper.username.label": "Nom d'utilisateur Instapaper",
|
||||||
|
"integration.obsidian.enable.description": "Afficher le bouton 'Enregistrer dans Obsidian' si disponible.",
|
||||||
|
"integration.obsidian.enable.label": "Activer",
|
||||||
|
"integration.obsidian.title": "Obsidian",
|
||||||
|
"integration.obsidian.vaultPath.description": "Le chemin vers votre coffre Obsidian.",
|
||||||
|
"integration.obsidian.vaultPath.label": "Chemin du coffre Obsidian",
|
||||||
|
"integration.outline.collection.description": "L'UUID ou l'urlId de la collection où les documents sont enregistrés.",
|
||||||
|
"integration.outline.collection.label": "Collection Outline",
|
||||||
|
"integration.outline.enable.description": "Afficher le bouton 'Enregistrer dans Outline' si disponible.",
|
||||||
|
"integration.outline.enable.label": "Activer",
|
||||||
|
"integration.outline.endpoint.description": "L'URL est 'https://<VOTRE_DOMAINE_OUTLINE>/api'.",
|
||||||
|
"integration.outline.endpoint.label": "URL de base de l'API Outline",
|
||||||
|
"integration.outline.title": "Outline",
|
||||||
|
"integration.outline.token.description": "Vous pouvez l'obtenir dans les paramètres de votre compte Outline.",
|
||||||
|
"integration.outline.token.label": "Clé API Outline",
|
||||||
|
"integration.qbittorrent.enable.description": "Afficher le bouton 'Télécharger avec qBittorrent' si disponible.",
|
||||||
|
"integration.qbittorrent.enable.label": "Activer",
|
||||||
|
"integration.qbittorrent.host.description": "L'URL de votre WebUI qBittorrent, ex. http://localhost:8080.",
|
||||||
|
"integration.qbittorrent.host.label": "Hôte qBittorrent",
|
||||||
|
"integration.qbittorrent.password.label": "Mot de passe qBittorrent",
|
||||||
|
"integration.qbittorrent.title": "qBittorrent",
|
||||||
|
"integration.qbittorrent.username.label": "Nom d'utilisateur qBittorrent",
|
||||||
|
"integration.readeck.enable.description": "Afficher le bouton 'Enregistrer dans Readeck' si disponible.",
|
||||||
|
"integration.readeck.enable.label": "Activer",
|
||||||
|
"integration.readeck.endpoint.description": "L'URL est 'https://<VOTRE_DOMAINE_READECK>'.",
|
||||||
|
"integration.readeck.endpoint.label": "URL de base de l'API Readeck",
|
||||||
|
"integration.readeck.title": "Readeck",
|
||||||
|
"integration.readeck.token.description": "Vous pouvez l'obtenir dans les paramètres de votre compte Readeck.",
|
||||||
|
"integration.readeck.token.label": "Jeton API Readeck",
|
||||||
|
"integration.readwise.enable.description": "Afficher le bouton 'Enregistrer dans Readwise' si disponible.",
|
||||||
|
"integration.readwise.enable.label": "Activer",
|
||||||
|
"integration.readwise.title": "Readwise",
|
||||||
|
"integration.readwise.token.description": "Vous pouvez l'obtenir ici :",
|
||||||
|
"integration.readwise.token.label": "Jeton d'accès Readwise",
|
||||||
|
"integration.save_ai_summary_as_description.label": "Enregistrer le résumé IA comme description",
|
||||||
|
"integration.search.placeholder": "Rechercher des intégrations...",
|
||||||
|
"integration.sidebar_title": "Intégration",
|
||||||
|
"integration.status.configured": "Configuré",
|
||||||
|
"integration.status.enabled": "Activé",
|
||||||
|
"integration.tip": "Conseil : Vos données sensibles sont stockées localement et ne sont pas téléchargées sur le serveur.",
|
||||||
|
"integration.title": "Intégration",
|
||||||
|
"integration.use_browser_fetch.description": "Utiliser l'API fetch du navigateur pour les intégrations personnalisées au lieu du fetch natif d'Electron. Activer pour une meilleure compatibilité web, désactiver pour une sécurité renforcée.",
|
||||||
|
"integration.use_browser_fetch.label": "Utiliser Browser Fetch",
|
||||||
|
"integration.zotero.enable.description": "Afficher le bouton 'Enregistrer dans Zotero' si disponible.",
|
||||||
|
"integration.zotero.enable.label": "Activer",
|
||||||
|
"integration.zotero.title": "Zotero",
|
||||||
|
"integration.zotero.token.description": "Jeton API Zotero, vous pouvez l'obtenir ici :",
|
||||||
|
"integration.zotero.token.label": "Jeton API Zotero",
|
||||||
|
"integration.zotero.userID.description": "ID utilisateur Zotero, vous pouvez l'obtenir ici :",
|
||||||
|
"integration.zotero.userID.label": "ID utilisateur Zotero",
|
||||||
|
"invitation.activate": "Activer",
|
||||||
|
"invitation.codeOptions.betaUser": "1. Trouvez un utilisateur bêta qui vous invite.",
|
||||||
|
"invitation.codeOptions.discord": "2. Rejoignez notre serveur Discord et obtenez occasionnellement des cadeaux.",
|
||||||
|
"invitation.codeOptions.xAccount": "3. Suivez notre compte X, et obtenez des cadeaux de temps en temps.",
|
||||||
|
"invitation.confirmModal.cancel": "Annuler",
|
||||||
|
"invitation.confirmModal.confirm": "Voulez-vous continuer ?",
|
||||||
|
"invitation.confirmModal.continue": "Continuer",
|
||||||
|
"invitation.confirmModal.message": "Générer un code d'invitation vous coûtera {{INVITATION_PRICE}} <PowerIcon /> Puissance.",
|
||||||
|
"invitation.confirmModal.title": "Confirmer",
|
||||||
|
"invitation.created_at": "Créé le",
|
||||||
|
"invitation.earlyAccess": "Folo nécessite actuellement un code d'invitation pour être utilisé.",
|
||||||
|
"invitation.earlyAccessMessage": "😰 Désolé, Folo nécessite actuellement un code d'invitation pour être utilisé.",
|
||||||
|
"invitation.generate": "Générer",
|
||||||
|
"invitation.generateButton": "Générer un nouveau code",
|
||||||
|
"invitation.generateCost": "Vous pouvez dépenser {{INVITATION_PRICE}} <PowerIcon /> Puissance pour générer un code d'invitation pour vos amis.",
|
||||||
|
"invitation.getCodeMessage": "Vous pouvez obtenir un code d'invitation via les méthodes suivantes :",
|
||||||
|
"invitation.limitationMessage": "En fonction de votre temps d'utilisation, vous pouvez générer jusqu'à {{limitation}} codes d'invitation.",
|
||||||
|
"invitation.newInvitationSuccess": "🎉 Nouvelle invitation générée, code copié",
|
||||||
|
"invitation.noInvitations": "Aucune invitation",
|
||||||
|
"invitation.notUsed": "Non utilisé",
|
||||||
|
"invitation.sidebar_title": "Invitations",
|
||||||
|
"invitation.tableHeaders.code": "Code",
|
||||||
|
"invitation.tableHeaders.creationTime": "Date de création",
|
||||||
|
"invitation.tableHeaders.usedBy": "Utilisé par",
|
||||||
|
"invitation.title": "Code d'invitation",
|
||||||
|
"lists.create": "Créer une nouvelle liste",
|
||||||
|
"lists.created.error": "Échec de la création de la liste.",
|
||||||
|
"lists.created.success": "Liste créée avec succès !",
|
||||||
|
"lists.delete.confirm": "Confirmer la suppression de la liste ?",
|
||||||
|
"lists.delete.error": "Échec de la suppression de la liste.",
|
||||||
|
"lists.delete.success": "Liste supprimée avec succès !",
|
||||||
|
"lists.delete.warning": "Attention : Une fois supprimée, la liste ne sera plus disponible et tout le contenu sera définitivement supprimé et irrécupérable !..",
|
||||||
|
"lists.description": "Description",
|
||||||
|
"lists.earnings": "Gagner",
|
||||||
|
"lists.edit.error": "Échec de la modification de la liste.",
|
||||||
|
"lists.edit.label": "Modifier",
|
||||||
|
"lists.edit.success": "Liste modifiée avec succès !",
|
||||||
|
"lists.fee.description": "Les frais que les autres doivent vous payer pour s'abonner à cette liste.",
|
||||||
|
"lists.fee.label": "Frais",
|
||||||
|
"lists.feeds.actions": "Actions",
|
||||||
|
"lists.feeds.add.error": "Échec de l'ajout du flux à la liste.",
|
||||||
|
"lists.feeds.add.label": "Ajouter",
|
||||||
|
"lists.feeds.add.success": "Flux ajouté à la liste.",
|
||||||
|
"lists.feeds.delete.error": "Échec de la suppression du flux de la liste.",
|
||||||
|
"lists.feeds.delete.success": "Flux supprimé de la liste.",
|
||||||
|
"lists.feeds.id": "ID du flux",
|
||||||
|
"lists.feeds.label": "Flux",
|
||||||
|
"lists.feeds.manage": "Gérer les flux",
|
||||||
|
"lists.feeds.owner": "Propriétaire",
|
||||||
|
"lists.feeds.search": "Rechercher un flux",
|
||||||
|
"lists.feeds.title": "Titre",
|
||||||
|
"lists.image": "Image",
|
||||||
|
"lists.info": "Les listes sont des collections de flux que vous pouvez partager ou vendre pour que d'autres s'y abonnent. Les abonnés synchroniseront et accéderont à tous les flux de la liste.",
|
||||||
|
"lists.manage_list": "Gérer la liste",
|
||||||
|
"lists.noLists": "Aucune liste",
|
||||||
|
"lists.select_feeds": "Sélectionner les flux à ajouter à la liste actuelle",
|
||||||
|
"lists.submit": "Soumettre",
|
||||||
|
"lists.subscriptions": "Membres",
|
||||||
|
"lists.title": "Titre",
|
||||||
|
"lists.view": "Vue",
|
||||||
|
"notifications.channel": "Canal",
|
||||||
|
"notifications.current": "(client actuel)",
|
||||||
|
"notifications.info": "Folo offre des fonctionnalités de notification robustes et polyvalentes via <ActionsLink>Actions</ActionsLink>. Vous pouvez personnaliser la notification pour des flux, des vues ou des mots-clés spécifiques. Ci-dessous vos canaux de notification enregistrés.",
|
||||||
|
"notifications.test": "Notification de test",
|
||||||
|
"notifications.test_success": "Notification de test envoyée avec succès.",
|
||||||
|
"notifications.token": "Jeton client",
|
||||||
|
"plan.canceled_expires": "Annulé - Expire le {{date}}",
|
||||||
|
"plan.current_plan": "Forfait actuel",
|
||||||
|
"plan.descriptions.basic": "Plus de flux sans fonctionnalités IA.",
|
||||||
|
"plan.descriptions.free": "Idéal pour les débutants.",
|
||||||
|
"plan.descriptions.plus": "Débloquez les fonctionnalités IA et plus de flux.",
|
||||||
|
"plan.descriptions.pro": "Accès complet au meilleur de Folo.",
|
||||||
|
"plan.featureValues.AI_MODEL_SELECTION.curated": "Modèles sélectionnés",
|
||||||
|
"plan.featureValues.AI_MODEL_SELECTION.high_performance": "Tous les modèles haut de gamme",
|
||||||
|
"plan.featureValues.AI_MODEL_SELECTION.none": "—",
|
||||||
|
"plan.features.AI_BRING_YOUR_OWN_KEY": "IA Apportez votre propre clé",
|
||||||
|
"plan.features.AI_CREDIT": "Crédits de discussion IA",
|
||||||
|
"plan.features.AI_MODEL_SELECTION": "Sélection du modèle IA",
|
||||||
|
"plan.features.BOOSTS": "Accélération de l'actualisation du flux",
|
||||||
|
"plan.features.INTEGRATION_SUPPORTED": "Intégrations tierces",
|
||||||
|
"plan.features.MAX_ACTIONS": "Actions",
|
||||||
|
"plan.features.MAX_AI_ENTRY_SUMMARY_PER_DAY": "Résumés IA par jour",
|
||||||
|
"plan.features.MAX_AI_ENTRY_TRANSLATION_PER_DAY": "Traductions IA par jour",
|
||||||
|
"plan.features.MAX_AI_REQUESTS_PER_DAY": "Discussions IA par jour",
|
||||||
|
"plan.features.MAX_AI_REQUESTS_PER_MONTH": "Discussions IA par mois",
|
||||||
|
"plan.features.MAX_AI_TASKS": "Tâches IA",
|
||||||
|
"plan.features.MAX_AI_TEXT_TO_SPEECH_PER_DAY": "Synthèse vocale IA par jour",
|
||||||
|
"plan.features.MAX_INBOXES": "Boîte de réception",
|
||||||
|
"plan.features.MAX_LISTS": "Liste",
|
||||||
|
"plan.features.MAX_RSSHUB_SUBSCRIPTIONS": "Abonnements RSSHub",
|
||||||
|
"plan.features.MAX_SUBSCRIPTIONS": "Abonnements aux flux",
|
||||||
|
"plan.features.PRIORITY_SUPPORT": "Support prioritaire",
|
||||||
|
"plan.features.PRIVATE_SUBSCRIPTION": "Abonnements privés",
|
||||||
|
"plan.features.SECURE_IMAGE_PROXY": "Proxy d'image sécurisé",
|
||||||
|
"plan.manage_subscription": "Gérer l'abonnement",
|
||||||
|
"plan.renews": "Renouvellement le {{date}}",
|
||||||
|
"plan.trial_ends": "L'essai se termine le {{date}}",
|
||||||
|
"privacy.privacy": "Confidentialité",
|
||||||
|
"privacy.terms": "Conditions",
|
||||||
|
"profile.avatar.cropInstructions": "Faites glisser la zone de recadrage pour ajuster votre avatar",
|
||||||
|
"profile.avatar.dropZoneSubtext": "ou cliquez pour sélectionner depuis votre ordinateur",
|
||||||
|
"profile.avatar.dropZoneText": "Glissez et déposez une image ici",
|
||||||
|
"profile.avatar.fileTooLarge": "La taille du fichier doit être inférieure à {{size}}",
|
||||||
|
"profile.avatar.invalidFileType": "Veuillez sélectionner un fichier image valide",
|
||||||
|
"profile.avatar.label": "Avatar",
|
||||||
|
"profile.avatar.processingError": "Erreur de traitement de l'image",
|
||||||
|
"profile.avatar.selectAnother": "Sélectionner une autre",
|
||||||
|
"profile.avatar.selectFile": "Sélectionner un fichier",
|
||||||
|
"profile.avatar.uploadError": "Échec du téléchargement de l'avatar",
|
||||||
|
"profile.avatar.uploadSuccess": "Avatar téléchargé avec succès",
|
||||||
|
"profile.avatar.uploadTitle": "Télécharger un avatar",
|
||||||
|
"profile.change_password.label": "Changer le mot de passe",
|
||||||
|
"profile.confirm_password.label": "Confirmer le mot de passe",
|
||||||
|
"profile.current_password.label": "Mot de passe actuel",
|
||||||
|
"profile.danger_zone": "Zone de danger",
|
||||||
|
"profile.delete_account.label": "Supprimer le compte",
|
||||||
|
"profile.edit_email": "Modifier l'email",
|
||||||
|
"profile.edit_profile": "Modifier le profil",
|
||||||
|
"profile.email.change": "Changer d'email",
|
||||||
|
"profile.email.change_note": "Si vous souhaitez changer d'email, vous devez vérifier votre nouvel email.",
|
||||||
|
"profile.email.changed": "Email changé.",
|
||||||
|
"profile.email.changed_verification_sent": "L'email de vérification pour le nouvel email a été envoyé.",
|
||||||
|
"profile.email.label": "Email",
|
||||||
|
"profile.email.send_verification": "Envoyer l'email de vérification",
|
||||||
|
"profile.email.unverified": "Non vérifié",
|
||||||
|
"profile.email.verification_sent": "Email de vérification envoyé",
|
||||||
|
"profile.email.verified": "Vérifié",
|
||||||
|
"profile.email.verify_email": "Veuillez vérifier votre email ({{email_address}}) pour continuer",
|
||||||
|
"profile.email.verify_status": "Votre email est {{status}}",
|
||||||
|
"profile.handle.description": "Votre identifiant unique.",
|
||||||
|
"profile.handle.label": "Identifiant",
|
||||||
|
"profile.link_social.authentication": "Authentication",
|
||||||
|
"profile.link_social.link": "Lier",
|
||||||
|
"profile.link_social.unlink.success": "Compte social délié.",
|
||||||
|
"profile.name.description": "Votre nom d'affichage public.",
|
||||||
|
"profile.name.label": "Nom d'affichage",
|
||||||
|
"profile.new_password.label": "Nouveau mot de passe",
|
||||||
|
"profile.no_password": "<Link>Réinitialisez</Link> votre mot de passe pour en définir un nouveau.",
|
||||||
|
"profile.password.label": "Mot de passe",
|
||||||
|
"profile.profile.bio": "Bio",
|
||||||
|
"profile.profile.bio_placeholder": "Parlez-nous de vous...",
|
||||||
|
"profile.profile.changed": "Profil mis à jour",
|
||||||
|
"profile.profile.save": "Enregistrer",
|
||||||
|
"profile.profile.social_links": "Liens sociaux",
|
||||||
|
"profile.profile.social_links_discord": "discord ID",
|
||||||
|
"profile.profile.social_links_facebook": "Facebook ID",
|
||||||
|
"profile.profile.social_links_github": "Github ID",
|
||||||
|
"profile.profile.social_links_instagram": "Instagram ID",
|
||||||
|
"profile.profile.social_links_twitter": "Twitter ID",
|
||||||
|
"profile.profile.social_links_youtube": "Youtube ID",
|
||||||
|
"profile.profile.website": "Site web",
|
||||||
|
"profile.reset_password_mail_sent": "Email de réinitialisation de mot de passe envoyé.",
|
||||||
|
"profile.security": "Sécurité",
|
||||||
|
"profile.set_avatar": "Définir l'avatar",
|
||||||
|
"profile.sidebar_title": "Profil",
|
||||||
|
"profile.submit": "Soumettre",
|
||||||
|
"profile.title": "Paramètres de profil",
|
||||||
|
"profile.totp_code.init": "Scannez le code QR avec votre application TOTP",
|
||||||
|
"profile.totp_code.invalid": "Code TOTP invalide.",
|
||||||
|
"profile.totp_code.label": "Code TOTP",
|
||||||
|
"profile.totp_code.title": "Entrer le code TOTP",
|
||||||
|
"profile.two_factor.disable": "Désactiver 2FA",
|
||||||
|
"profile.two_factor.disabled": "Authentification à deux facteurs désactivée.",
|
||||||
|
"profile.two_factor.enable": "Activer 2FA",
|
||||||
|
"profile.two_factor.enable_notice": "Vous devez activer l'authentification à deux facteurs pour effectuer cette action.",
|
||||||
|
"profile.two_factor.enabled": "Authentification à deux facteurs activée.",
|
||||||
|
"profile.two_factor.label": "Deux facteurs",
|
||||||
|
"profile.two_factor.no_password": "Vous devez <Link>définir</Link> un mot de passe avant d'activer le 2FA.",
|
||||||
|
"profile.updateSuccess": "Profil mis à jour.",
|
||||||
|
"profile.update_password_success": "Mot de passe mis à jour.",
|
||||||
|
"referral.description": "Partagez Folo avec un ami ! Prolongez votre aperçu Pro et vos avantages futurs, et vos amis pourront également bénéficier d'une période d'essai de 45 jours. <Link>En savoir plus</Link>.",
|
||||||
|
"referral.invited_friend_status.pending": "En attente de validation",
|
||||||
|
"referral.invited_friend_status.valid": "Valide",
|
||||||
|
"referral.link": "Votre lien d'invitation :",
|
||||||
|
"referral.pro_status.preview": "Votre statut Aperçu Pro : Expire le {{dateString}} ({{daysLeft}} jours restants)",
|
||||||
|
"referral.pro_status.trial": "Votre niveau actuel : Gratuit",
|
||||||
|
"referral.pro_status.user": "Votre statut Aperçu Pro : Actif",
|
||||||
|
"rsshub.addModal.access_key_label": "Clé d'accès (Optionnel)",
|
||||||
|
"rsshub.addModal.add": "Ajouter",
|
||||||
|
"rsshub.addModal.base_url_label": "URL de base",
|
||||||
|
"rsshub.addModal.description": "Pour utiliser votre propre instance dans Folo, vous devez lui ajouter les variables d'environnement suivantes.",
|
||||||
|
"rsshub.add_new_instance": "Ajouter une nouvelle instance",
|
||||||
|
"rsshub.description": "RSSHub est un réseau RSS open-source géré par la communauté. Folo fournit une instance dédiée intégrée et l'utilise pour prendre en charge des milliers de contenus d'abonnement, vous pouvez également obtenir une acquisition de contenu plus stable en utilisant vos propres instances ou des instances tierces.",
|
||||||
|
"rsshub.public_instances": "Instances disponibles",
|
||||||
|
"rsshub.table.delete.confirm": "Êtes-vous sûr de vouloir supprimer cette instance ?",
|
||||||
|
"rsshub.table.delete.label": "Supprimer",
|
||||||
|
"rsshub.table.delete.success": "Instance supprimée avec succès.",
|
||||||
|
"rsshub.table.description": "Description",
|
||||||
|
"rsshub.table.edit": "Modifier",
|
||||||
|
"rsshub.table.inuse": "En cours d'utilisation",
|
||||||
|
"rsshub.table.limit_reached": "Limite atteinte",
|
||||||
|
"rsshub.table.official": "Officiel",
|
||||||
|
"rsshub.table.owner": "Propriétaire",
|
||||||
|
"rsshub.table.price": "Prix mensuel",
|
||||||
|
"rsshub.table.private": "Privé",
|
||||||
|
"rsshub.table.unavailable": "Indisponible",
|
||||||
|
"rsshub.table.unlimited": "Illimité",
|
||||||
|
"rsshub.table.use": "Utiliser",
|
||||||
|
"rsshub.table.userCount": "Nombre d'utilisateurs",
|
||||||
|
"rsshub.table.userLimit": "Limite d'utilisateurs",
|
||||||
|
"rsshub.table.yours": "Le vôtre",
|
||||||
|
"rsshub.useModal.about": "À propos de cette instance",
|
||||||
|
"rsshub.useModal.month": "mois",
|
||||||
|
"rsshub.useModal.months_label": "Le nombre de mois que vous souhaitez acheter",
|
||||||
|
"rsshub.useModal.purchase_expires_at": "Vous avez acheté cette instance, et votre achat expire le",
|
||||||
|
"rsshub.useModal.title": "Instance RSSHub",
|
||||||
|
"rsshub.useModal.useWith": "Utiliser avec {{amount}} <Power />",
|
||||||
|
"subscription.actions.comingSoon": "Bientôt disponible",
|
||||||
|
"subscription.actions.current": "Plan actuel",
|
||||||
|
"subscription.actions.manage_error": "Une erreur s'est produite lors de l'ouverture de la gestion de l'abonnement.",
|
||||||
|
"subscription.actions.upgrade": "Mettre à niveau",
|
||||||
|
"subscription.actions.upgrade_error": "Une erreur s'est produite lors du démarrage du paiement.",
|
||||||
|
"subscription.badge.popular": "Le plus populaire",
|
||||||
|
"subscription.billing.monthly": "Mensuel",
|
||||||
|
"subscription.billing.yearly": "Annuel",
|
||||||
|
"subscription.billing.yearly_savings": "Économisez {{value}}%",
|
||||||
|
"subscription.discount.tag": "Économisez {{value}}%",
|
||||||
|
"subscription.feature.included": "Inclus",
|
||||||
|
"subscription.price.free": "Gratuit",
|
||||||
|
"subscription.price.per_month": "par mois",
|
||||||
|
"subscription.price.per_month_billed_yearly": "par mois, facturé annuellement",
|
||||||
|
"subscription.summary.active": "Vous avez un abonnement actif.",
|
||||||
|
"subscription.summary.current": "Plan {{plan}}",
|
||||||
|
"subscription.summary.free": "Plan gratuit",
|
||||||
|
"subscription.summary.free_description": "Mettez à niveau pour débloquer plus de flux, d'actions et de fonctionnalités IA.",
|
||||||
|
"subscription.summary.title": "Votre abonnement",
|
||||||
|
"subscription.summary.trial_expiring": "L'essai se termine le {{date}} ({{days}} jours restants)",
|
||||||
|
"subscription.unavailable": "Les abonnements ne sont pas disponibles pour le moment.",
|
||||||
|
"titles.about": "À propos",
|
||||||
|
"titles.account": "Compte",
|
||||||
|
"titles.actions": "Actions",
|
||||||
|
"titles.ai": "IA",
|
||||||
|
"titles.appearance": "Apparence",
|
||||||
|
"titles.data_control": "Contrôle des données",
|
||||||
|
"titles.feeds": "Flux",
|
||||||
|
"titles.general": "Général",
|
||||||
|
"titles.integration": "Intégration",
|
||||||
|
"titles.invitations": "Invitations",
|
||||||
|
"titles.lists": "Listes",
|
||||||
|
"titles.notifications": "Notifications",
|
||||||
|
"titles.plan.long": "Mettre à niveau votre plan",
|
||||||
|
"titles.plan.short": "Plan",
|
||||||
|
"titles.power": "Puissance",
|
||||||
|
"titles.privacy": "Confidentialité",
|
||||||
|
"titles.referral.long": "Inviter des amis & Prolongez Pro",
|
||||||
|
"titles.referral.short": "Inviter & Gagner",
|
||||||
|
"titles.shortcuts": "Raccourcis",
|
||||||
|
"titles.sign_out": "Se déconnecter",
|
||||||
|
"titles.subscription.long": "Gérer votre abonnement",
|
||||||
|
"titles.subscription.short": "Abonnement",
|
||||||
|
"titles.token_usage": "Utilisation des crédits IA",
|
||||||
|
"wallet.balance.activePoints": "Points actifs",
|
||||||
|
"wallet.balance.dailyReward": "Votre récompense quotidienne",
|
||||||
|
"wallet.balance.title": "Votre solde",
|
||||||
|
"wallet.balance.withdrawable": "Retirable",
|
||||||
|
"wallet.balance.withdrawableTooltip": "La puissance retirable comprend à la fois les pourboires que vous avez reçus et la puissance que vous avez rechargée.",
|
||||||
|
"wallet.claim.button.claim": "Réclamer la puissance quotidienne",
|
||||||
|
"wallet.claim.button.claimed": "Réclamé aujourd'hui",
|
||||||
|
"wallet.claim.tooltip.alreadyClaimed": "Vous avez déjà réclamé aujourd'hui.",
|
||||||
|
"wallet.claim.tooltip.canClaim": "Réclamez votre {{amount}} Puissance quotidienne maintenant !",
|
||||||
|
"wallet.create.button": "Créer un portefeuille",
|
||||||
|
"wallet.create.description": "Créez un portefeuille gratuit pour recevoir de la <PowerIcon /> <strong>Puissance</strong>, qui peut être utilisée pour récompenser les créateurs et aussi être récompensé pour vos contributions de contenu.",
|
||||||
|
"wallet.power.dailyClaim": "Vous pouvez réclamer {{amount}} Puissance gratuite quotidiennement, qui peut être utilisée pour donner des pourboires aux entrées RSS sur Folo.",
|
||||||
|
"wallet.power.rewardDescription": "Tous les utilisateurs actifs sur Folo sont éligibles aux récompenses quotidiennes de puissance.",
|
||||||
|
"wallet.power.rewardDescription2": "En fonction de votre niveau et de vos activités passées, vous pouvez recevoir une <Balance /> récompense aujourd'hui. <Link>En savoir plus.</Link>",
|
||||||
|
"wallet.ranking.level": "Niveau",
|
||||||
|
"wallet.ranking.name": "Nom",
|
||||||
|
"wallet.ranking.power": "Puissance",
|
||||||
|
"wallet.ranking.rank": "Rang",
|
||||||
|
"wallet.ranking.title": "Classement de puissance",
|
||||||
|
"wallet.rewardDescription.description1": "Les récompenses quotidiennes pour chaque utilisateur sont basées sur deux facteurs : le niveau de l'utilisateur et les points d'activité de l'utilisateur.",
|
||||||
|
"wallet.rewardDescription.description2": "Niveau utilisateur : Déterminé par le classement de puissance de l'utilisateur par rapport à tous les autres utilisateurs.",
|
||||||
|
"wallet.rewardDescription.description3": "Activité utilisateur : S'engager avec diverses fonctionnalités de Folo peut augmenter l'activité. Les récompenses vont d'un minimum de 1x à un maximum de 5x.",
|
||||||
|
"wallet.rewardDescription.level": "Niveau utilisateur",
|
||||||
|
"wallet.rewardDescription.percentage": "Pourcentage de classement",
|
||||||
|
"wallet.rewardDescription.reward": "Multiplicateur de récompense",
|
||||||
|
"wallet.rewardDescription.title": "Description de la récompense",
|
||||||
|
"wallet.rewardDescription.total": "Récompense totale par jour",
|
||||||
|
"wallet.sidebar_title": "Puissance",
|
||||||
|
"wallet.transactions.amount": "Montant",
|
||||||
|
"wallet.transactions.date": "Date",
|
||||||
|
"wallet.transactions.from": "De",
|
||||||
|
"wallet.transactions.more": "Voir plus via l'explorateur de blockchain.",
|
||||||
|
"wallet.transactions.noTransactions": "Aucune transaction",
|
||||||
|
"wallet.transactions.title": "Transactions",
|
||||||
|
"wallet.transactions.to": "À",
|
||||||
|
"wallet.transactions.tx": "Tx",
|
||||||
|
"wallet.transactions.type": "Type",
|
||||||
|
"wallet.transactions.types.airdrop": "Airdrop",
|
||||||
|
"wallet.transactions.types.all": "Tout",
|
||||||
|
"wallet.transactions.types.burn": "Brûler",
|
||||||
|
"wallet.transactions.types.mint": "Mint",
|
||||||
|
"wallet.transactions.types.purchase": "Achat",
|
||||||
|
"wallet.transactions.types.tip": "Pourboire",
|
||||||
|
"wallet.transactions.types.withdraw": "Retrait",
|
||||||
|
"wallet.transactions.you": "Vous",
|
||||||
|
"wallet.withdraw.addressLabel": "Votre adresse Ethereum",
|
||||||
|
"wallet.withdraw.amountLabel": "Montant",
|
||||||
|
"wallet.withdraw.availableBalance": "Vous avez <Balance></Balance> puissance retirable dans votre portefeuille.",
|
||||||
|
"wallet.withdraw.button": "Retirer",
|
||||||
|
"wallet.withdraw.error": "Retrait échoué : {{error}}",
|
||||||
|
"wallet.withdraw.modalTitle": "Retirer de la puissance",
|
||||||
|
"wallet.withdraw.receiveRSS3": "Vous recevrez {{amount}} RSS3",
|
||||||
|
"wallet.withdraw.submitButton": "Soumettre",
|
||||||
|
"wallet.withdraw.success": "Retrait réussi !",
|
||||||
|
"wallet.withdraw.toRss3Label": "Retirer en tant que RSS3"
|
||||||
|
}
|
||||||
|
|
@ -131,6 +131,9 @@
|
||||||
"appearance.custom_css.label": "Custom CSS",
|
"appearance.custom_css.label": "Custom CSS",
|
||||||
"appearance.custom_font": "カスタムフォント",
|
"appearance.custom_font": "カスタムフォント",
|
||||||
"appearance.customization.title": "カスタマイズ",
|
"appearance.customization.title": "カスタマイズ",
|
||||||
|
"appearance.customize_sub_tabs.description": "購読タブを好みに合わせてカスタマイズします。",
|
||||||
|
"appearance.customize_sub_tabs.label": "購読タブをカスタマイズ",
|
||||||
|
"appearance.customize_toolbar.description": "エントリーコンテンツツールバーを好みに合わせてカスタマイズします。",
|
||||||
"appearance.customize_toolbar.label": "ツールバーをカスタマイズ",
|
"appearance.customize_toolbar.label": "ツールバーをカスタマイズ",
|
||||||
"appearance.date_format.description": "表示日付形式を調整します。",
|
"appearance.date_format.description": "表示日付形式を調整します。",
|
||||||
"appearance.date_format.label": "日付形式",
|
"appearance.date_format.label": "日付形式",
|
||||||
|
|
@ -210,6 +213,7 @@
|
||||||
"appearance.unread_count.view_and_subscription.label": "サイドバーに表示",
|
"appearance.unread_count.view_and_subscription.label": "サイドバーに表示",
|
||||||
"appearance.use_pointer_cursor.description": "マウスがインタラクティブな要素の上にあるとき、カーソルは手の形になります。",
|
"appearance.use_pointer_cursor.description": "マウスがインタラクティブな要素の上にあるとき、カーソルは手の形になります。",
|
||||||
"appearance.use_pointer_cursor.label": "手の形のカーソルを使用する",
|
"appearance.use_pointer_cursor.label": "手の形のカーソルを使用する",
|
||||||
|
"appearance.words.customize": "カスタマイズ",
|
||||||
"common.give_star": "<HeartIcon />私たちの製品が好きですか?<Link>GitHub で Star を付けましょう!</Link>",
|
"common.give_star": "<HeartIcon />私たちの製品が好きですか?<Link>GitHub で Star を付けましょう!</Link>",
|
||||||
"control.paid_badge.basic_or_higher": "この機能を利用するには Basic プラン以上が必要です",
|
"control.paid_badge.basic_or_higher": "この機能を利用するには Basic プラン以上が必要です",
|
||||||
"control.paid_badge.free_limited": "この機能は無料プランでは制限されています",
|
"control.paid_badge.free_limited": "この機能は無料プランでは制限されています",
|
||||||
|
|
@ -564,6 +568,7 @@
|
||||||
"plan.descriptions.pro": "Foloの最高の機能すべてにアクセス。",
|
"plan.descriptions.pro": "Foloの最高の機能すべてにアクセス。",
|
||||||
"plan.featureValues.AI_MODEL_SELECTION.curated": "厳選モデル",
|
"plan.featureValues.AI_MODEL_SELECTION.curated": "厳選モデル",
|
||||||
"plan.featureValues.AI_MODEL_SELECTION.high_performance": "高性能すべて",
|
"plan.featureValues.AI_MODEL_SELECTION.high_performance": "高性能すべて",
|
||||||
|
"plan.featureValues.AI_MODEL_SELECTION.none": "—",
|
||||||
"plan.features.AI_BRING_YOUR_OWN_KEY": "AI Bring Your Own Key",
|
"plan.features.AI_BRING_YOUR_OWN_KEY": "AI Bring Your Own Key",
|
||||||
"plan.features.AI_CREDIT": "AI Chat クレジット",
|
"plan.features.AI_CREDIT": "AI Chat クレジット",
|
||||||
"plan.features.AI_MODEL_SELECTION": "AI モデル選択",
|
"plan.features.AI_MODEL_SELECTION": "AI モデル選択",
|
||||||
|
|
@ -739,6 +744,7 @@
|
||||||
"titles.sign_out": "サインアウト",
|
"titles.sign_out": "サインアウト",
|
||||||
"titles.subscription.long": "サブスクリプション管理",
|
"titles.subscription.long": "サブスクリプション管理",
|
||||||
"titles.subscription.short": "サブスクリプション",
|
"titles.subscription.short": "サブスクリプション",
|
||||||
|
"titles.token_usage": "トークン使用状況",
|
||||||
"wallet.balance.activePoints": "アクティブなポイント",
|
"wallet.balance.activePoints": "アクティブなポイント",
|
||||||
"wallet.balance.dailyReward": "デイリー報酬",
|
"wallet.balance.dailyReward": "デイリー報酬",
|
||||||
"wallet.balance.title": "残高",
|
"wallet.balance.title": "残高",
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
|
"about.aiOnboardingDescription": "重新体验交互式新手引导。",
|
||||||
"about.appTip": "App 功能",
|
"about.appTip": "App 功能",
|
||||||
"about.appTipDescription": "App 功能介绍与使用指南。",
|
"about.appTipDescription": "App 功能介绍与使用指南。",
|
||||||
"about.changelog": "更新日志",
|
"about.changelog": "更新日志",
|
||||||
|
|
@ -130,6 +131,9 @@
|
||||||
"appearance.custom_css.label": "自定义 CSS",
|
"appearance.custom_css.label": "自定义 CSS",
|
||||||
"appearance.custom_font": "自定义字体",
|
"appearance.custom_font": "自定义字体",
|
||||||
"appearance.customization.title": "自定义",
|
"appearance.customization.title": "自定义",
|
||||||
|
"appearance.customize_sub_tabs.description": "按你的偏好自定义订阅视图标签。",
|
||||||
|
"appearance.customize_sub_tabs.label": "自定义视图标签",
|
||||||
|
"appearance.customize_toolbar.description": "按你的偏好自定义条目内容工具栏。",
|
||||||
"appearance.customize_toolbar.label": "自定义工具栏",
|
"appearance.customize_toolbar.label": "自定义工具栏",
|
||||||
"appearance.date_format.description": "调整显示使用的日期格式。",
|
"appearance.date_format.description": "调整显示使用的日期格式。",
|
||||||
"appearance.date_format.label": "日期格式",
|
"appearance.date_format.label": "日期格式",
|
||||||
|
|
@ -209,6 +213,7 @@
|
||||||
"appearance.unread_count.view_and_subscription.label": "在视图和订阅源上显示",
|
"appearance.unread_count.view_and_subscription.label": "在视图和订阅源上显示",
|
||||||
"appearance.use_pointer_cursor.description": "当鼠标悬停在任何可交互元素上时,光标会显示为手型光标。",
|
"appearance.use_pointer_cursor.description": "当鼠标悬停在任何可交互元素上时,光标会显示为手型光标。",
|
||||||
"appearance.use_pointer_cursor.label": "使用手型光标",
|
"appearance.use_pointer_cursor.label": "使用手型光标",
|
||||||
|
"appearance.words.customize": "自定义",
|
||||||
"common.give_star": "<HeartIcon />喜欢我们的产品吗? <Link>在 GitHub 上给我们「Star」吧!</Link>",
|
"common.give_star": "<HeartIcon />喜欢我们的产品吗? <Link>在 GitHub 上给我们「Star」吧!</Link>",
|
||||||
"control.paid_badge.basic_or_higher": "此功能需要 Basic 计划或更高级别才能使用",
|
"control.paid_badge.basic_or_higher": "此功能需要 Basic 计划或更高级别才能使用",
|
||||||
"control.paid_badge.free_limited": "此功能在免费计划中有限制",
|
"control.paid_badge.free_limited": "此功能在免费计划中有限制",
|
||||||
|
|
@ -563,6 +568,7 @@
|
||||||
"plan.descriptions.pro": "完整享受 Folo 的最佳功能。",
|
"plan.descriptions.pro": "完整享受 Folo 的最佳功能。",
|
||||||
"plan.featureValues.AI_MODEL_SELECTION.curated": "精选模型",
|
"plan.featureValues.AI_MODEL_SELECTION.curated": "精选模型",
|
||||||
"plan.featureValues.AI_MODEL_SELECTION.high_performance": "全部高性能模型",
|
"plan.featureValues.AI_MODEL_SELECTION.high_performance": "全部高性能模型",
|
||||||
|
"plan.featureValues.AI_MODEL_SELECTION.none": "—",
|
||||||
"plan.features.AI_BRING_YOUR_OWN_KEY": "AI 自定义 API 密钥",
|
"plan.features.AI_BRING_YOUR_OWN_KEY": "AI 自定义 API 密钥",
|
||||||
"plan.features.AI_CREDIT": "AI Chat 积分",
|
"plan.features.AI_CREDIT": "AI Chat 积分",
|
||||||
"plan.features.AI_MODEL_SELECTION": "AI 模型选择",
|
"plan.features.AI_MODEL_SELECTION": "AI 模型选择",
|
||||||
|
|
@ -738,6 +744,7 @@
|
||||||
"titles.sign_out": "登出",
|
"titles.sign_out": "登出",
|
||||||
"titles.subscription.long": "管理订阅",
|
"titles.subscription.long": "管理订阅",
|
||||||
"titles.subscription.short": "订阅",
|
"titles.subscription.short": "订阅",
|
||||||
|
"titles.token_usage": "AI 积分使用情况",
|
||||||
"wallet.balance.activePoints": "活跃度",
|
"wallet.balance.activePoints": "活跃度",
|
||||||
"wallet.balance.dailyReward": "每日奖励",
|
"wallet.balance.dailyReward": "每日奖励",
|
||||||
"wallet.balance.title": "余额",
|
"wallet.balance.title": "余额",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
{
|
||||||
|
"category.entry": "Entrée",
|
||||||
|
"category.entry_render": "Rendu de l'entrée",
|
||||||
|
"category.global": "Global",
|
||||||
|
"category.integration": "Intégration",
|
||||||
|
"category.layout": "Mise en page",
|
||||||
|
"category.list": "Liste",
|
||||||
|
"category.settings": "Paramètres",
|
||||||
|
"category.subscription": "Abonnement",
|
||||||
|
"category.timeline": "Chronologie",
|
||||||
|
"command.entry.next_entry.description": "Passer à l'entrée suivante selon la chronologie actuelle.",
|
||||||
|
"command.entry.next_entry.title": "Entrée suivante",
|
||||||
|
"command.entry.previous_entry.description": "Passer à l'entrée précédente selon la chronologie actuelle.",
|
||||||
|
"command.entry.previous_entry.title": "Entrée précédente",
|
||||||
|
"command.entry.scroll_down.description": "Faire défiler vers le bas dans le rendu de l'entrée.",
|
||||||
|
"command.entry.scroll_down.title": "Défiler vers le bas",
|
||||||
|
"command.entry.scroll_up.description": "Faire défiler vers le haut dans le rendu de l'entrée.",
|
||||||
|
"command.entry.scroll_up.title": "Défiler vers le haut",
|
||||||
|
"command.global.quick_add.description": "Ouvrir le panneau d'ajout rapide pour suivre un nouveau flux ou autre.",
|
||||||
|
"command.global.quick_add.title": "Ajout rapide",
|
||||||
|
"command.global.quick_search.description": "Ouvrir le panneau de recherche rapide.",
|
||||||
|
"command.global.quick_search.title": "Recherche rapide",
|
||||||
|
"command.global.show_shortcuts.description": "Afficher le guide des raccourcis.",
|
||||||
|
"command.global.show_shortcuts.title": "Afficher les raccourcis",
|
||||||
|
"command.global.toggle_ai_chat.description": "Basculer le panneau de discussion IA.",
|
||||||
|
"command.global.toggle_ai_chat.title": "Discussion IA",
|
||||||
|
"command.global.toggle_corner_play.description": "Lecture/Pause (Si de l'audio est en cours de lecture en arrière-plan).",
|
||||||
|
"command.global.toggle_corner_play.title": "Basculer la lecture en coin",
|
||||||
|
"command.layout.focus_to_entry_render.title": "Focus sur le rendu de l'entrée",
|
||||||
|
"command.layout.focus_to_subscription.title": "Focus sur l'abonnement",
|
||||||
|
"command.layout.focus_to_timeline.title": "Focus sur la chronologie",
|
||||||
|
"command.layout.toggle_subscription_column.description": "Afficher/Masquer la colonne d'abonnement dans la mise en page.",
|
||||||
|
"command.layout.toggle_subscription_column.title": "Basculer la colonne d'abonnement",
|
||||||
|
"command.subscription.mark_all_as_read.title": "Tout marquer comme lu",
|
||||||
|
"command.subscription.next_subscription.description": "Abonnement suivant",
|
||||||
|
"command.subscription.next_subscription.title": "Abonnement suivant",
|
||||||
|
"command.subscription.open_in_browser.title": "Ouvrir dans le navigateur",
|
||||||
|
"command.subscription.open_in_tab.title": "Ouvrir dans un onglet",
|
||||||
|
"command.subscription.open_site_in_browser.title": "Ouvrir le site dans le navigateur",
|
||||||
|
"command.subscription.open_site_in_tab.title": "Ouvrir le site dans un onglet",
|
||||||
|
"command.subscription.previous_subscription.description": "Abonnement précédent",
|
||||||
|
"command.subscription.previous_subscription.title": "Abonnement précédent",
|
||||||
|
"command.subscription.switch_between_views.title": "Basculer entre les vues",
|
||||||
|
"command.subscription.switch_next_view.title": "Passer à la vue suivante",
|
||||||
|
"command.subscription.switch_previous_view.title": "Passer à la vue précédente",
|
||||||
|
"command.subscription.switch_tab_to_article.description": "Passer à l'onglet article dans la vue d'abonnement.",
|
||||||
|
"command.subscription.switch_tab_to_article.title": "Passer à l'onglet Article",
|
||||||
|
"command.subscription.switch_tab_to_audio.description": "Passer à l'onglet audio dans la vue d'abonnement.",
|
||||||
|
"command.subscription.switch_tab_to_audio.title": "Passer à l'onglet Audio",
|
||||||
|
"command.subscription.switch_tab_to_next.description": "Passer à l'onglet suivant dans la vue d'abonnement.",
|
||||||
|
"command.subscription.switch_tab_to_next.title": "Passer à l'onglet Suivant",
|
||||||
|
"command.subscription.switch_tab_to_notification.description": "Passer à l'onglet notification dans la vue d'abonnement.",
|
||||||
|
"command.subscription.switch_tab_to_notification.title": "Passer à l'onglet Notification",
|
||||||
|
"command.subscription.switch_tab_to_picture.description": "Passer à l'onglet image dans la vue d'abonnement.",
|
||||||
|
"command.subscription.switch_tab_to_picture.title": "Passer à l'onglet Image",
|
||||||
|
"command.subscription.switch_tab_to_previous.description": "Passer à l'onglet précédent dans la vue d'abonnement.",
|
||||||
|
"command.subscription.switch_tab_to_previous.title": "Passer à l'onglet Précédent",
|
||||||
|
"command.subscription.switch_tab_to_social.description": "Passer à l'onglet social dans la vue d'abonnement.",
|
||||||
|
"command.subscription.switch_tab_to_social.title": "Passer à l'onglet Social",
|
||||||
|
"command.subscription.switch_tab_to_video.description": "Passer à l'onglet vidéo dans la vue d'abonnement.",
|
||||||
|
"command.subscription.switch_tab_to_video.title": "Passer à l'onglet Vidéo",
|
||||||
|
"command.subscription.toggle_folder_collapse.description": "Développer/Réduire le dossier sélectionné dans la vue d'abonnement.",
|
||||||
|
"command.subscription.toggle_folder_collapse.title": "Basculer le dossier",
|
||||||
|
"command.timeline.refetch.description": "Rafraîchir la chronologie actuelle.",
|
||||||
|
"command.timeline.refetch.title": "Rafraîchir",
|
||||||
|
"command.timeline.switch_to_next.description": "Passer à l'élément suivant de la chronologie.",
|
||||||
|
"command.timeline.switch_to_next.title": "Passer à la chronologie suivante",
|
||||||
|
"command.timeline.switch_to_previous.description": "Passer à l'élément précédent de la chronologie.",
|
||||||
|
"command.timeline.switch_to_previous.title": "Passer à la chronologie précédente",
|
||||||
|
"command.timeline.toggle_unread_only.description": "Activer/Désactiver le mode non lu uniquement dans la chronologie.",
|
||||||
|
"command.timeline.toggle_unread_only.title": "Basculer Non lu uniquement",
|
||||||
|
"settings.shortcuts.conflict": "Conflit de raccourci",
|
||||||
|
"settings.shortcuts.conflict_command": "Ce raccourci entre en conflit avec la commande :",
|
||||||
|
"settings.shortcuts.custom": "Personnalisé",
|
||||||
|
"settings.shortcuts.custom_content": "Ce raccourci est personnalisé par vous",
|
||||||
|
"settings.shortcuts.description": "Personnalisez les raccourcis de l'application. Vous trouverez ci-dessous une liste de commandes personnalisables.",
|
||||||
|
"settings.shortcuts.press_to_record": "Appuyez sur les touches pour enregistrer",
|
||||||
|
"settings.shortcuts.reset": "Réinitialiser",
|
||||||
|
"settings.shortcuts.undo": "Annuler"
|
||||||
|
}
|
||||||
|
|
@ -18,6 +18,8 @@
|
||||||
"command.entry.scroll_up.title": "上にスクロール",
|
"command.entry.scroll_up.title": "上にスクロール",
|
||||||
"command.global.quick_add.description": "新しいフィードやその他をフォローするためのクイック追加パネルを開きます。",
|
"command.global.quick_add.description": "新しいフィードやその他をフォローするためのクイック追加パネルを開きます。",
|
||||||
"command.global.quick_add.title": "クイック追加",
|
"command.global.quick_add.title": "クイック追加",
|
||||||
|
"command.global.quick_search.description": "クイック検索パネルを開きます。",
|
||||||
|
"command.global.quick_search.title": "クイック検索",
|
||||||
"command.global.show_shortcuts.description": "ショートカットガイドラインモーダルを表示します",
|
"command.global.show_shortcuts.description": "ショートカットガイドラインモーダルを表示します",
|
||||||
"command.global.show_shortcuts.title": "ショートカットを表示",
|
"command.global.show_shortcuts.title": "ショートカットを表示",
|
||||||
"command.global.toggle_ai_chat.description": "AI チャットパネルを切り替えます",
|
"command.global.toggle_ai_chat.description": "AI チャットパネルを切り替えます",
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,12 @@
|
||||||
"command.entry.scroll_up.title": "向上滚动",
|
"command.entry.scroll_up.title": "向上滚动",
|
||||||
"command.global.quick_add.description": "打开快速添加面板以订阅新的订阅源或其他内容。",
|
"command.global.quick_add.description": "打开快速添加面板以订阅新的订阅源或其他内容。",
|
||||||
"command.global.quick_add.title": "快速添加",
|
"command.global.quick_add.title": "快速添加",
|
||||||
|
"command.global.quick_search.description": "打开快速搜索面板。",
|
||||||
|
"command.global.quick_search.title": "快速搜索",
|
||||||
"command.global.show_shortcuts.description": "显示快捷键指南。",
|
"command.global.show_shortcuts.description": "显示快捷键指南。",
|
||||||
"command.global.show_shortcuts.title": "显示快捷键",
|
"command.global.show_shortcuts.title": "显示快捷键",
|
||||||
|
"command.global.toggle_ai_chat.description": "切换 AI 聊天面板。",
|
||||||
|
"command.global.toggle_ai_chat.title": "AI 聊天",
|
||||||
"command.global.toggle_corner_play.description": "播放或暂停播放状态(如果当前有音频在后台播放)。",
|
"command.global.toggle_corner_play.description": "播放或暂停播放状态(如果当前有音频在后台播放)。",
|
||||||
"command.global.toggle_corner_play.title": "切换边角播放",
|
"command.global.toggle_corner_play.title": "切换边角播放",
|
||||||
"command.layout.focus_to_entry_render.title": "聚焦到条目内容",
|
"command.layout.focus_to_entry_render.title": "聚焦到条目内容",
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
export type SupportedActionLanguage = "en" | "ja" | "zh-CN" | "zh-TW"
|
export type SupportedActionLanguage = "en" | "ja" | "zh-CN" | "zh-TW" | "fr-FR"
|
||||||
export const ACTION_LANGUAGE_MAP: Record<
|
export const ACTION_LANGUAGE_MAP: Record<
|
||||||
SupportedActionLanguage,
|
SupportedActionLanguage,
|
||||||
{
|
{
|
||||||
|
|
@ -27,5 +27,23 @@ export const ACTION_LANGUAGE_MAP: Record<
|
||||||
value: "ja",
|
value: "ja",
|
||||||
code: "jpn",
|
code: "jpn",
|
||||||
},
|
},
|
||||||
|
"fr-FR": {
|
||||||
|
label: "Français (France)",
|
||||||
|
value: "fr-FR",
|
||||||
|
code: "fra",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
export const ACTION_LANGUAGE_KEYS = Object.keys(ACTION_LANGUAGE_MAP) as SupportedActionLanguage[]
|
export const ACTION_LANGUAGE_KEYS = Object.keys(ACTION_LANGUAGE_MAP) as SupportedActionLanguage[]
|
||||||
|
|
||||||
|
export type ApiSupportedActionLanguage = Exclude<SupportedActionLanguage, "fr-FR">
|
||||||
|
|
||||||
|
export const toApiSupportedActionLanguage = (
|
||||||
|
language: SupportedActionLanguage,
|
||||||
|
): ApiSupportedActionLanguage => {
|
||||||
|
if (language === "fr-FR") {
|
||||||
|
// Server-side AI language enum does not include French yet.
|
||||||
|
return "en"
|
||||||
|
}
|
||||||
|
|
||||||
|
return language
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ export interface UISettings {
|
||||||
showUnreadCountBadgeMobile: boolean
|
showUnreadCountBadgeMobile: boolean
|
||||||
|
|
||||||
// Discover
|
// Discover
|
||||||
discoverLanguage: "all" | "eng" | "cmn"
|
discoverLanguage: "all" | "eng" | "cmn" | "fra"
|
||||||
|
|
||||||
// Desktop: Timeline tabs preset (excluding the first fixed tab)
|
// Desktop: Timeline tabs preset (excluding the first fixed tab)
|
||||||
timelineTabs: {
|
timelineTabs: {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import type { SummarySchema } from "@follow/database/schemas/types"
|
import type { SummarySchema } from "@follow/database/schemas/types"
|
||||||
import { summaryService } from "@follow/database/services/summary"
|
import { summaryService } from "@follow/database/services/summary"
|
||||||
import type { SupportedActionLanguage } from "@follow/shared"
|
import type { SupportedActionLanguage } from "@follow/shared"
|
||||||
|
import { toApiSupportedActionLanguage } from "@follow/shared"
|
||||||
|
|
||||||
import { api } from "../../context"
|
import { api } from "../../context"
|
||||||
import type { Hydratable, Resetable } from "../../lib/base"
|
import type { Hydratable, Resetable } from "../../lib/base"
|
||||||
|
|
@ -172,7 +173,7 @@ class SummarySyncService {
|
||||||
const pendingPromise = api()
|
const pendingPromise = api()
|
||||||
.ai.summary({
|
.ai.summary({
|
||||||
id: entryId,
|
id: entryId,
|
||||||
language: actionLanguage,
|
language: toApiSupportedActionLanguage(actionLanguage),
|
||||||
target,
|
target,
|
||||||
})
|
})
|
||||||
.then((summary) => {
|
.then((summary) => {
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { UserRole } from "@follow/constants"
|
||||||
import type { TranslationSchema } from "@follow/database/schemas/types"
|
import type { TranslationSchema } from "@follow/database/schemas/types"
|
||||||
import { TranslationService } from "@follow/database/services/translation"
|
import { TranslationService } from "@follow/database/services/translation"
|
||||||
import type { SupportedActionLanguage } from "@follow/shared"
|
import type { SupportedActionLanguage } from "@follow/shared"
|
||||||
|
import { toApiSupportedActionLanguage } from "@follow/shared"
|
||||||
import { checkLanguage } from "@follow/utils/language"
|
import { checkLanguage } from "@follow/utils/language"
|
||||||
import { create, indexedResolver, windowScheduler } from "@yornaath/batshit"
|
import { create, indexedResolver, windowScheduler } from "@yornaath/batshit"
|
||||||
|
|
||||||
|
|
@ -171,7 +172,7 @@ class TranslationSyncService {
|
||||||
try {
|
try {
|
||||||
const request: TranslationBatchRequest & { mode?: TranslationMode } = {
|
const request: TranslationBatchRequest & { mode?: TranslationMode } = {
|
||||||
ids: group.ids,
|
ids: group.ids,
|
||||||
language: group.language,
|
language: toApiSupportedActionLanguage(group.language),
|
||||||
fields: group.fields,
|
fields: group.fields,
|
||||||
mode: group.mode,
|
mode: group.mode,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue