From 8f1efb256f2cc95571088a1e19e83d49c1cbbfac Mon Sep 17 00:00:00 2001 From: Innei Date: Tue, 24 Sep 2024 20:16:42 +0800 Subject: [PATCH 01/10] refactor(electron): global caller Signed-off-by: Innei --- apps/main/src/lib/cleaner.ts | 8 +++--- apps/main/src/tipc/app.ts | 10 +++++--- apps/main/src/tipc/reader.ts | 20 ++++++--------- apps/main/src/window.ts | 14 +++++++---- packages/shared/src/bridge.ts | 47 +++++++++++++++++++++-------------- 5 files changed, 57 insertions(+), 42 deletions(-) diff --git a/apps/main/src/lib/cleaner.ts b/apps/main/src/lib/cleaner.ts index 190aecaf92..18832311c0 100644 --- a/apps/main/src/lib/cleaner.ts +++ b/apps/main/src/lib/cleaner.ts @@ -1,4 +1,4 @@ -import { callGlobalContextMethod } from "@follow/shared/bridge" +import { callWindowExpose } from "@follow/shared/bridge" import { dialog } from "electron" import { getMainWindow } from "~/window" @@ -17,6 +17,7 @@ export const clearAllData = async () => { message: t("dialog.clearAllData"), buttons: [t("dialog.yes"), t("dialog.no")], }) + const caller = callWindowExpose(win) if (result.response === 1) { return @@ -36,11 +37,12 @@ export const clearAllData = async () => { "cookies", ], }) - callGlobalContextMethod(win, "toast.success", ["App data reset successfully"]) + + caller.toast.success("App data reset successfully") // reload the app win.reload() } catch (error: any) { - callGlobalContextMethod(win, "toast.error", [`Error resetting app data: ${error.message}`]) + caller.toast.error(`Error resetting app data: ${error.message}`) } } diff --git a/apps/main/src/tipc/app.ts b/apps/main/src/tipc/app.ts index ef6c4afaab..58761890f5 100644 --- a/apps/main/src/tipc/app.ts +++ b/apps/main/src/tipc/app.ts @@ -1,7 +1,7 @@ import path from "node:path" import { getRendererHandlers } from "@egoist/tipc/main" -import { callGlobalContextMethod } from "@follow/shared/bridge" +import { callWindowExpose } from "@follow/shared/bridge" import type { BrowserWindow } from "electron" import { app, clipboard, dialog, screen } from "electron" @@ -199,13 +199,17 @@ export const appRoute = { await downloadFile(input, result.filePath).catch((err) => { const senderWindow = (sender as Sender).getOwnerBrowserWindow() if (!senderWindow) return - callGlobalContextMethod(senderWindow, "toast.error", ["Download failed!"]) + callWindowExpose(senderWindow).toast.error("Download failed!", { + duration: 1000, + }) throw err }) const senderWindow = (sender as Sender).getOwnerBrowserWindow() if (!senderWindow) return - callGlobalContextMethod(senderWindow, "toast.success", ["Download success!"]) + callWindowExpose(senderWindow).toast.success("Download success!", { + duration: 1000, + }) }), getAppPath: t.procedure.action(async () => app.getAppPath()), diff --git a/apps/main/src/tipc/reader.ts b/apps/main/src/tipc/reader.ts index c403a8bf26..d21aa085c2 100644 --- a/apps/main/src/tipc/reader.ts +++ b/apps/main/src/tipc/reader.ts @@ -2,7 +2,7 @@ import fs from "node:fs" import { createRequire } from "node:module" import path from "node:path" -import { callGlobalContextMethod } from "@follow/shared/bridge" +import { callWindowExpose } from "@follow/shared/bridge" import { app, BrowserWindow } from "electron" import { MsEdgeTTS, OUTPUT_FORMAT } from "msedge-tts" @@ -54,12 +54,9 @@ export const readerRoute = { if (!window) { return } - callGlobalContextMethod(window, "toast.error", [ - error.message, - { - duration: 1000, - }, - ]) + callWindowExpose(window).toast.error(error.message, { + duration: 1000, + }) } }), @@ -70,12 +67,9 @@ export const readerRoute = { } await tts.setMetadata(input, OUTPUT_FORMAT.WEBM_24KHZ_16BIT_MONO_OPUS).catch((error) => { - callGlobalContextMethod(window, "toast.error", [ - error.message, - { - duration: 1000, - }, - ]) + callWindowExpose(window).toast.error(error.message, { + duration: 1000, + }) }) }), diff --git a/apps/main/src/window.ts b/apps/main/src/window.ts index ce4145c755..3a4ffacf74 100644 --- a/apps/main/src/window.ts +++ b/apps/main/src/window.ts @@ -2,7 +2,7 @@ import path from "node:path" import { fileURLToPath } from "node:url" import { is } from "@electron-toolkit/utils" -import { callGlobalContextMethod } from "@follow/shared/bridge" +import { callWindowExpose } from "@follow/shared/bridge" import { imageRefererMatches } from "@follow/shared/image" import type { BrowserWindowConstructorOptions } from "electron" import { BrowserWindow, screen, shell } from "electron" @@ -219,7 +219,8 @@ export const createMainWindow = () => { window.hide() } - callGlobalContextMethod(window, "electronClose") + const caller = callWindowExpose(window) + caller.electronClose() } else { windows.mainWindow = null } @@ -228,11 +229,13 @@ export const createMainWindow = () => { window.on("show", () => { cancelPollingUpdateUnreadCount() - callGlobalContextMethod(window, "electronShow") + const caller = callWindowExpose(window) + caller.electronShow() }) window.on("hide", async () => { - const settings = await callGlobalContextMethod(window, "getUISettings") + const caller = callWindowExpose(window) + const settings = await caller.getUISettings() if (settings.showDockBadge) { pollingUpdateUnreadCount() @@ -247,7 +250,8 @@ export const createSettingWindow = (path?: string) => { // if we open a new window then the state between the two windows will be out of sync. if (windows.mainWindow && windows.mainWindow.isVisible()) { windows.mainWindow.show() - callGlobalContextMethod(windows.mainWindow, "showSetting", [path]) + + callWindowExpose(windows.mainWindow).showSetting(path) return } if (windows.settingWindow) { diff --git a/packages/shared/src/bridge.ts b/packages/shared/src/bridge.ts index 31a6a3237b..d7192b3e2c 100644 --- a/packages/shared/src/bridge.ts +++ b/packages/shared/src/bridge.ts @@ -4,6 +4,7 @@ import type { toast } from "sonner" import type { GeneralSettings, UISettings } from "./interface/settings" const PREFIX = "__follow" + interface RenderGlobalContext { showSetting: (path?: string) => void getGeneralSettings: () => GeneralSettings @@ -19,22 +20,32 @@ export const registerGlobalContext = (context: RenderGlobalContext) => { globalThis[PREFIX] = context } -export function callGlobalContextMethod( - window: BrowserWindow, - method: T, - - // @ts-expect-error - args: Parameters = [] as any, -): Promise> -export function callGlobalContextMethod(window: BrowserWindow, method: string, args?: any[]): void - -export function callGlobalContextMethod( - window: BrowserWindow, - method: T, - - args: Parameters = [] as any, -) { - return window.webContents.executeJavaScript( - `globalThis.${PREFIX}.${method}(${args.map((arg) => JSON.stringify(arg)).join(",")})`, - ) +function createProxy(window: BrowserWindow, path: string[] = []): T { + return new Proxy((() => {}) as any, { + get(_, prop: string) { + const newPath = [...path, prop] + + return createProxy(window, newPath) + }, + apply(_, __, args: any[]) { + const methodPath = path.join(".") + + return window.webContents.executeJavaScript( + `globalThis.${PREFIX}.${methodPath}(${args.map((arg) => JSON.stringify(arg)).join(",")})`, + ) + }, + }) +} +type AddPromise = T extends (...args: infer A) => Promise + ? (...args: A) => Promise + : T extends (...args: infer A) => infer R + ? (...args: A) => Promise> + : any + +type Fn = { + [K in keyof T]: AddPromise & + (T[K] extends object ? { [P in keyof T[K]]: AddPromise } : never) +} +export function callWindowExpose(window: BrowserWindow) { + return createProxy(window) as Fn } From 5933ceab63de0a7a46e85d0b841acbf893d8fcec Mon Sep 17 00:00:00 2001 From: Innei Date: Tue, 24 Sep 2024 20:31:48 +0800 Subject: [PATCH 02/10] fix(invitation): reset app data then logout Signed-off-by: Innei --- apps/main/src/lib/cleaner.ts | 13 ++++++++++--- apps/main/src/menu.ts | 4 ++-- .../renderer/src/modules/auth/LoginModalContent.tsx | 2 +- apps/renderer/src/pages/(external)/invitation.tsx | 10 ++++++++-- 4 files changed, 21 insertions(+), 8 deletions(-) diff --git a/apps/main/src/lib/cleaner.ts b/apps/main/src/lib/cleaner.ts index 18832311c0..bd7d4883b4 100644 --- a/apps/main/src/lib/cleaner.ts +++ b/apps/main/src/lib/cleaner.ts @@ -5,10 +5,9 @@ import { getMainWindow } from "~/window" import { t } from "./i18n" -export const clearAllData = async () => { +export const clearAllDataAndConfirm = async () => { const win = getMainWindow() if (!win) return - const ses = win.webContents.session // Dialog to confirm const result = await dialog.showMessageBox({ @@ -17,11 +16,19 @@ export const clearAllData = async () => { message: t("dialog.clearAllData"), buttons: [t("dialog.yes"), t("dialog.no")], }) - const caller = callWindowExpose(win) if (result.response === 1) { return } + return clearAllData() +} + +export const clearAllData = async () => { + const win = getMainWindow() + if (!win) return + const ses = win.webContents.session + const caller = callWindowExpose(win) + try { await ses.clearCache() diff --git a/apps/main/src/menu.ts b/apps/main/src/menu.ts index 5217438a1d..e985df608e 100644 --- a/apps/main/src/menu.ts +++ b/apps/main/src/menu.ts @@ -4,7 +4,7 @@ import type { BrowserWindow, MenuItem, MenuItemConstructorOptions } from "electr import { Menu } from "electron" import { isDev, isMacOS } from "./env" -import { clearAllData } from "./lib/cleaner" +import { clearAllDataAndConfirm } from "./lib/cleaner" import { t } from "./lib/i18n" import { revealLogFile } from "./logger" import { checkForUpdates, quitAndInstall } from "./updater" @@ -38,7 +38,7 @@ export const registerAppMenu = () => { { type: "separator" }, { label: t("menu.clearAllData"), - click: clearAllData, + click: clearAllDataAndConfirm, }, { role: "quit", label: t("menu.quit", { name }) }, ], diff --git a/apps/renderer/src/modules/auth/LoginModalContent.tsx b/apps/renderer/src/modules/auth/LoginModalContent.tsx index ed31594c2d..1d699381f1 100644 --- a/apps/renderer/src/modules/auth/LoginModalContent.tsx +++ b/apps/renderer/src/modules/auth/LoginModalContent.tsx @@ -105,7 +105,7 @@ const LoginButtonContent = (props: { children: React.ReactNode; isLoading: boole ) : ( { - signOut() - window.location.href = "/" + if (window.electron) { + tipcClient?.clearAllData().then(() => { + window.location.href = "/" + }) + } else { + signOut() + } }} > From 95d17f573283f816d22cdb4655cdfba4a1354b17 Mon Sep 17 00:00:00 2001 From: Innei Date: Tue, 24 Sep 2024 20:46:49 +0800 Subject: [PATCH 03/10] fix: adjust slide up modal aspect ratio Signed-off-by: Innei --- .../components/ui/modal/stacked/custom-modal.tsx | 16 ++++++++++++++-- apps/renderer/src/modules/achievement/hooks.tsx | 1 + 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/apps/renderer/src/components/ui/modal/stacked/custom-modal.tsx b/apps/renderer/src/components/ui/modal/stacked/custom-modal.tsx index 73ed7bc7f3..c1ab28d61d 100644 --- a/apps/renderer/src/components/ui/modal/stacked/custom-modal.tsx +++ b/apps/renderer/src/components/ui/modal/stacked/custom-modal.tsx @@ -12,7 +12,12 @@ export const PlainModal = ({ children }: PropsWithChildren) => children export { PlainModal as NoopChildren } -export const SlideUpModal = (props: PropsWithChildren) => { +type ModalTemplateType = { + (props: PropsWithChildren<{ className?: string }>): JSX.Element + class: (className: string) => (props: PropsWithChildren<{ className?: string }>) => JSX.Element +} + +export const SlideUpModal: ModalTemplateType = (props) => { const winHeight = useState(() => window.innerHeight)[0] const { dismiss } = useCurrentModal() return ( @@ -39,7 +44,8 @@ export const SlideUpModal = (props: PropsWithChildren) => { }} className={cn( "relative flex flex-col items-center overflow-hidden rounded-xl border bg-theme-background p-8 pb-0", - "h-[80vh] w-[600px] max-w-full shadow lg:max-h-[calc(100vh-10rem)]", + "aspect-[7/9] w-[600px] max-w-full shadow lg:max-h-[calc(100vh-10rem)]", + props.className, )} > {props.children} @@ -49,3 +55,9 @@ export const SlideUpModal = (props: PropsWithChildren) => { ) } + +SlideUpModal.class = (className: string) => { + return (props: ComponentType) => ( + + ) +} diff --git a/apps/renderer/src/modules/achievement/hooks.tsx b/apps/renderer/src/modules/achievement/hooks.tsx index 6e0489ce96..170ba52198 100644 --- a/apps/renderer/src/modules/achievement/hooks.tsx +++ b/apps/renderer/src/modules/achievement/hooks.tsx @@ -42,6 +42,7 @@ export const useAchievementModal = () => { title: "Achievements", content: AchievementModalContent, CustomModalComponent: SlideUpModal, + overlay: true, }) }, [present]) } From 68ad4483a40bc088878b995c05285ff418d895d7 Mon Sep 17 00:00:00 2001 From: Innei Date: Tue, 24 Sep 2024 20:49:51 +0800 Subject: [PATCH 04/10] chore(i18n): `actions` translation Signed-off-by: Innei --- apps/renderer/src/modules/settings/tabs/lists.tsx | 2 +- locales/common/zh-CN.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/renderer/src/modules/settings/tabs/lists.tsx b/apps/renderer/src/modules/settings/tabs/lists.tsx index e2fa73eb57..da4865bffd 100644 --- a/apps/renderer/src/modules/settings/tabs/lists.tsx +++ b/apps/renderer/src/modules/settings/tabs/lists.tsx @@ -85,7 +85,7 @@ export const SettingLists = () => { {t.settings("lists.fee.label")} - + {t.common("words.actions")} diff --git a/locales/common/zh-CN.json b/locales/common/zh-CN.json index a6d242e22c..00d462cfe6 100644 --- a/locales/common/zh-CN.json +++ b/locales/common/zh-CN.json @@ -10,7 +10,7 @@ "time.today": "今天", "time.yesterday": "昨天", "tips.load-lng-error": "加载语言包失败", - "words.actions": "自动化", + "words.actions": "操作", "words.back": "返回", "words.copy": "复制", "words.edit": "编辑", From a61bd173a9310fd4c9a27b08fcd057dd4231a9e3 Mon Sep 17 00:00:00 2001 From: Innei Date: Tue, 24 Sep 2024 21:32:49 +0800 Subject: [PATCH 05/10] feat(lottie): star lottie Signed-off-by: Innei --- apps/renderer/global.d.ts | 7 ++ .../components/ui/lottie-container/index.tsx | 99 ++++++++++++++++++ .../src/hooks/biz/useEntryActions.tsx | 15 ++- apps/renderer/src/lottie/star.lottie | Bin 0 -> 2326 bytes .../entry-column/layouts/EntryItemWrapper.tsx | 2 +- .../(with-layout)/profile/[id]/index.tsx | 5 +- .../renderer/src/providers/root-providers.tsx | 2 + 7 files changed, 126 insertions(+), 4 deletions(-) create mode 100644 apps/renderer/src/components/ui/lottie-container/index.tsx create mode 100644 apps/renderer/src/lottie/star.lottie diff --git a/apps/renderer/global.d.ts b/apps/renderer/global.d.ts index 5553b75556..fbd50a0b90 100644 --- a/apps/renderer/global.d.ts +++ b/apps/renderer/global.d.ts @@ -9,4 +9,11 @@ declare global { platform: NodeJS.Platform } } + +declare module "react" { + export interface AriaAttributes { + "data-testid"?: string + } +} + export {} diff --git a/apps/renderer/src/components/ui/lottie-container/index.tsx b/apps/renderer/src/components/ui/lottie-container/index.tsx new file mode 100644 index 0000000000..0e51bb05ac --- /dev/null +++ b/apps/renderer/src/components/ui/lottie-container/index.tsx @@ -0,0 +1,99 @@ +import type { DotLottie } from "@lottiefiles/dotlottie-react" +import { DotLottieReact } from "@lottiefiles/dotlottie-react" +import { atom, useAtomValue } from "jotai" +import type { FC, ReactNode, RefCallback } from "react" +import { useEffect, useState } from "react" + +import { jotaiStore } from "~/lib/jotai" + +import { RootPortal } from "../portal" + +const portalElementsAtom = atom([] as ReactNode[]) + +export function LottieRenderContainer() { + const elements = useAtomValue(portalElementsAtom, { store: jotaiStore }) + + return ( + +
+ {elements.map((element) => element)} +
+
+ ) +} + +type LottieOptions = { + once?: boolean + + x: number + y: number + + height?: number + width?: number + className?: string + + onComplete?: () => void + + speed?: number +} +export const mountLottie = (url: string, options: LottieOptions) => { + const { once = true, height, width, x, y, className, speed } = options + + const Lottie: FC = () => { + const [dotLottie, setDotLottie] = useState(null) + + useEffect(() => { + function onComplete() { + if (once) { + unmount() + } + + options.onComplete?.() + } + + if (dotLottie) { + dotLottie.addEventListener("complete", onComplete) + } + + return () => { + if (dotLottie) { + dotLottie.removeEventListener("complete", onComplete) + } + } + }, [dotLottie]) + + const dotLottieRefCallback: RefCallback = (dotLottie) => { + setDotLottie(dotLottie) + } + + return ( + + ) + } + + const element = + const unmount = () => { + jotaiStore.set(portalElementsAtom, (prev) => prev.filter((e) => e !== element)) + } + + jotaiStore.set(portalElementsAtom, (prev) => [...prev, element]) + return unmount +} diff --git a/apps/renderer/src/hooks/biz/useEntryActions.tsx b/apps/renderer/src/hooks/biz/useEntryActions.tsx index 6ed9db3e66..a76015dd0f 100644 --- a/apps/renderer/src/hooks/biz/useEntryActions.tsx +++ b/apps/renderer/src/hooks/biz/useEntryActions.tsx @@ -14,6 +14,7 @@ import { } from "~/atoms/readability" import { useIntegrationSettingKey } from "~/atoms/settings/integration" import { whoami } from "~/atoms/user" +import { mountLottie } from "~/components/ui/lottie-container" import { SimpleIconsEagle, SimpleIconsInstapaper, @@ -23,12 +24,15 @@ import { shortcuts } from "~/constants/shortcuts" import { tipcClient } from "~/lib/client" import { nextFrame } from "~/lib/dom" import { getOS } from "~/lib/utils" +import StarAnimationUri from "~/lottie/star.lottie?url" import type { CombinedEntryModel } from "~/models" import { useTipModal } from "~/modules/wallet/hooks" import type { FlatEntryModel } from "~/store/entry" import { entryActions } from "~/store/entry" import { useFeedById } from "~/store/feed" +const absoluteStarAnimationUri = new URL(StarAnimationUri, import.meta.url).href + export const useEntryReadabilityToggle = ({ id, url }: { id: string; url: string }) => useCallback(async () => { const status = getReadabilityStatus()[id] @@ -165,7 +169,7 @@ export const useEntryActions = ({ hide?: boolean active?: boolean disabled?: boolean - onClick: () => void + onClick: (e: React.MouseEvent) => void }[] = [ { name: t("entry_actions.save_media_to_eagle"), @@ -295,7 +299,14 @@ export const useEntryActions = ({ name: t("entry_actions.star"), className: "i-mgc-star-cute-re", hide: !!populatedEntry.collections, - onClick: () => { + onClick: (e) => { + mountLottie(absoluteStarAnimationUri, { + x: e.clientX - 90, + y: e.clientY - 70, + height: 126, + width: 252, + }) + collect.mutate() }, }, diff --git a/apps/renderer/src/lottie/star.lottie b/apps/renderer/src/lottie/star.lottie new file mode 100644 index 0000000000000000000000000000000000000000..da36007d688715f52eecfc2f1f14590d24fd8d86 GIT binary patch literal 2326 zcmb7GXH*kd7N$f2H4usvr6r*SL+C-kmIRPU5rF}Tf(8jBlmtRaC?SAIuc0W2E7F33 zjAf9HQ5*yusnSKN(gYRIC@4(OopJY^oge$&dFS2oefQma|J-M1B?yq>+g`%#gDy35 z+9p#xdHVo)#}7{?c@hGH5Z-|layTS}Kn)~O$Pg2VEmzUIa3MiVylr<;C>F2l3+Ypl8A%Bo7`6^_NXqOIg=Q&q(*6o`Eh> z_aKrN%7a8DT-e4#OnA=(`V$B|22b+$#fREZJP15GnL_o$`|_~96pBB@#1rouNcf%u zWgGOjfk@$1@V7=H2N9@#q`)5)=rCLvcK>dM@b_k5CICtMg!uThZ29s z6&vH&rq^qI5sSMP>XQwZyNlLi2RA-(Q+J0Eg>Ijk6zw{!vcH~lLZVhCW8&P`XMO0K zVsMn{CiAp!MT0$lg#r7P-rM1QY+bk${Q1p5EZImX6aDnGWvNu@i|(tMg|g||-MJvT zlr*lQ5LgLuERQV)T$6Yp^ZW$CuyCjMbNa39tlk!(jcko829SOWy)+|d`ot}jhAY*b zB0Zn_hpD_yBW{CrzJ$9>v*9danHC86CD>F#YTn0kJYs8Xw1=|lQazmZD(hqMhqghp zoHMcveb?nTPEb9DGb+W7(gfGC$!)3ByU3~)0aOzkAEv20U!SNkmrDYzyRu(soSss> zL;u2Vx}A&k---g4Uswt~=VwGqc$J}gl+5PVB+K~*RL7LPw9{71LodvBD;Y(-wNXAC zZmn$N`i6KbE~PHQTKATuV+G{8>4NRzaNRPEVaBr0|82nh;i^>$JvP3K8DX6Jo*w8Vg_ z0aHS2J_FRL8td#G2@zS9HlMhJO45L03ev%?mP^)UEE%B~<(}GHV7~Gyt6jcW<|-Fz zY8I047BoBwe;XwuG<+lBt-nQxhQl>1Qu>OabSf-X@?B|*JRVe&EvjUAOa*8u8K`6>5z&e8Dns-91Osu>RJH*gGnd(vSnt3s zxO>L0eD=ZW)$6!$d6c3t8j+ED$)N4MPIXom<5$=W9{Ztx{96+Au-;9v9Q4hT^9F1% zjUiUXnPw;nIGBKpwaq*^7MP)sMN!1!wf(LW17f~H)#@12HchbQx5)jM&&pi`H!GC1 zV)EsR0qhhp%Z6Z~*~hbS1_lxIoe2Zt>0e5qHqg|8^0kua8^&A?Xp`9y4KA3XpPz%C z(x`GoMbmqtZ`5cvMi%zj%j_{q5#$PD)YtBzpdQf#Vl4=m;l?fOE*_GXiWBcamP{4u zi0+y$yhF1a(oehD)$C!v*jSRWkrc4Ub0&Z=*?VAjUFoiz6KhVZe7kEZdSR)Vil!WQ z9btff!L=S8@0$FniA2*gtBV)2u zW3sbh7GXR9pDdlm9U>;YNP>yFg^tNj;(e)RptLq|ubBjt1H`%UP}O_z6GHAyw8I{k z1r9DQ74CXW<8W}$Vq9Ly99|-rtj4d%E+BK#E~w4hFR11{8I|#n2!Ahbbs|15&J;{C zzqoMV_q^V@mxIrh6b~h8KP`_J;)6-a#v-~BQB(}6qAfWkZeA{I@_?L5)P+=cnU;M` z$PlFe&M5tG4x+^_Q@|F*-+W-d!_ww`zOj1zE>1HoLtv&3HbI20pHY`QJ~|V7%*_FM zd|wa<%4)2(liG>C=TQmO5($o&I(DpZFXX~g@b{^ zp`g~=iaC+$Va%1WcApRLAJL*I#JlBd9qKz2Taiitei(_FkLZd6#w>o6IUhapQ4|gW z!0&OaJfhi43JJwZy#a`CX|@l}!TV*{?Q6}|zSl)dxXUfr%Hxs@3$K`sPL16WbhG2k z_xzfScmscXIVA;{i3VjczSl=A=HtmldBJWLjT05Y^b_b~)ztXh?Og8sAcih=Y@h3L z`r7f8vSU;ZkS3BQsDGbbKDS{+JW>!pV zg@PN*f|V4#uaA^Y)=l(bm($r12KQ^5|23`f*;hpSD*4?@XDg*6TS9hL`~p&Z|NP(J nP1*lg|MmO$N&Vl``6my^|9{MCJF6W#x2XcW&Enl;;ce|t2e9{y literal 0 HcmV?d00001 diff --git a/apps/renderer/src/modules/entry-column/layouts/EntryItemWrapper.tsx b/apps/renderer/src/modules/entry-column/layouts/EntryItemWrapper.tsx index 60572a57df..53f25d616f 100644 --- a/apps/renderer/src/modules/entry-column/layouts/EntryItemWrapper.tsx +++ b/apps/renderer/src/modules/entry-column/layouts/EntryItemWrapper.tsx @@ -86,7 +86,7 @@ export const EntryItemWrapper: FC< .map((item) => ({ type: "text" as const, label: item.name, - click: item.onClick, + click: () => item.onClick(e), shortcut: item.shortcut, })), { diff --git a/apps/renderer/src/pages/(external)/(with-layout)/profile/[id]/index.tsx b/apps/renderer/src/pages/(external)/(with-layout)/profile/[id]/index.tsx index fbc6196e60..d60e8a788e 100644 --- a/apps/renderer/src/pages/(external)/(with-layout)/profile/[id]/index.tsx +++ b/apps/renderer/src/pages/(external)/(with-layout)/profile/[id]/index.tsx @@ -60,7 +60,10 @@ export function Component() {
{user.data?.handle}
-
+
{subscriptions.isLoading ? ( ) : ( diff --git a/apps/renderer/src/providers/root-providers.tsx b/apps/renderer/src/providers/root-providers.tsx index c14d309e2c..16715f979b 100644 --- a/apps/renderer/src/providers/root-providers.tsx +++ b/apps/renderer/src/providers/root-providers.tsx @@ -5,6 +5,7 @@ import { Provider } from "jotai" import type { FC, PropsWithChildren } from "react" import { HotkeysProvider } from "react-hotkeys-hook" +import { LottieRenderContainer } from "~/components/ui/lottie-container" import { ModalStackProvider } from "~/components/ui/modal" import { Toaster } from "~/components/ui/sonner" import { HotKeyScopeMap } from "~/constants" @@ -39,6 +40,7 @@ export const RootProviders: FC = ({ children }) => ( + {import.meta.env.DEV && } From d2c4e779367a7a76491f8f1c153ad851274573bf Mon Sep 17 00:00:00 2001 From: Jerry Wong Date: Tue, 24 Sep 2024 21:37:46 +0800 Subject: [PATCH 06/10] fix(i18n): modify some Arabic language file names (#611) * feat: add missing words in zh-HK * fix: modify some Arabic language file names. --- locales/app/{ar-iq.json => ar-IQ.json} | 0 locales/app/{ar-kw.json => ar-KW.json} | 0 locales/app/{ar-tn.json => ar-TN.json} | 0 locales/external/{ar-iq.json => ar-IQ.json} | 0 locales/external/{ar-tn.json => ar-TN.json} | 0 locales/native/{ar-iq.json => ar-IQ.json} | 0 locales/native/{ar-kw.json => ar-KW.json} | 0 locales/native/{ar-tn.json => ar-TN.json} | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename locales/app/{ar-iq.json => ar-IQ.json} (100%) rename locales/app/{ar-kw.json => ar-KW.json} (100%) rename locales/app/{ar-tn.json => ar-TN.json} (100%) rename locales/external/{ar-iq.json => ar-IQ.json} (100%) rename locales/external/{ar-tn.json => ar-TN.json} (100%) rename locales/native/{ar-iq.json => ar-IQ.json} (100%) rename locales/native/{ar-kw.json => ar-KW.json} (100%) rename locales/native/{ar-tn.json => ar-TN.json} (100%) diff --git a/locales/app/ar-iq.json b/locales/app/ar-IQ.json similarity index 100% rename from locales/app/ar-iq.json rename to locales/app/ar-IQ.json diff --git a/locales/app/ar-kw.json b/locales/app/ar-KW.json similarity index 100% rename from locales/app/ar-kw.json rename to locales/app/ar-KW.json diff --git a/locales/app/ar-tn.json b/locales/app/ar-TN.json similarity index 100% rename from locales/app/ar-tn.json rename to locales/app/ar-TN.json diff --git a/locales/external/ar-iq.json b/locales/external/ar-IQ.json similarity index 100% rename from locales/external/ar-iq.json rename to locales/external/ar-IQ.json diff --git a/locales/external/ar-tn.json b/locales/external/ar-TN.json similarity index 100% rename from locales/external/ar-tn.json rename to locales/external/ar-TN.json diff --git a/locales/native/ar-iq.json b/locales/native/ar-IQ.json similarity index 100% rename from locales/native/ar-iq.json rename to locales/native/ar-IQ.json diff --git a/locales/native/ar-kw.json b/locales/native/ar-KW.json similarity index 100% rename from locales/native/ar-kw.json rename to locales/native/ar-KW.json diff --git a/locales/native/ar-tn.json b/locales/native/ar-TN.json similarity index 100% rename from locales/native/ar-tn.json rename to locales/native/ar-TN.json From 3751363d45af3096ccefb81bf60ac72040fce505 Mon Sep 17 00:00:00 2001 From: Laitron Date: Tue, 24 Sep 2024 21:38:40 +0800 Subject: [PATCH 07/10] feat(i18n): added Turkish language (#544) Signed-off-by: Laiq --- apps/renderer/src/@types/constants.ts | 2 + apps/renderer/src/@types/default-resource.ts | 3 + locales/app/tr.json | 244 +++++++++++++++++++ locales/common/tr.json | 34 +++ locales/errors/tr.json | 29 +++ locales/external/tr.json | 36 +++ locales/lang/ar-DZ.json | 1 + locales/lang/ar-IQ.json | 1 + locales/lang/ar-KW.json | 1 + locales/lang/ar-MA.json | 1 + locales/lang/ar-TN.json | 1 + locales/lang/en.json | 1 + locales/lang/es.json | 1 + locales/lang/fi.json | 1 + locales/lang/fr.json | 1 + locales/lang/it.json | 1 + locales/lang/ja.json | 1 + locales/lang/ko.json | 1 + locales/lang/pt.json | 1 + locales/lang/ru.json | 1 + locales/lang/tr.json | 22 ++ locales/lang/zh-CN.json | 1 + locales/lang/zh-HK.json | 1 + locales/lang/zh-TW.json | 1 + locales/native/tr.json | 68 ++++++ locales/settings/tr.json | 236 ++++++++++++++++++ locales/shortcuts/tr.json | 24 ++ 27 files changed, 715 insertions(+) create mode 100644 locales/app/tr.json create mode 100644 locales/common/tr.json create mode 100644 locales/errors/tr.json create mode 100644 locales/external/tr.json create mode 100644 locales/lang/tr.json create mode 100644 locales/native/tr.json create mode 100644 locales/settings/tr.json create mode 100644 locales/shortcuts/tr.json diff --git a/apps/renderer/src/@types/constants.ts b/apps/renderer/src/@types/constants.ts index 8411b454ef..41ef8937a7 100644 --- a/apps/renderer/src/@types/constants.ts +++ b/apps/renderer/src/@types/constants.ts @@ -19,6 +19,7 @@ export const currentSupportedLanguages = [ "ru", "es", "ko", + "tr", ] export const dayjsLocaleImportMap = { en: ["en", () => import("dayjs/locale/en")], @@ -39,6 +40,7 @@ export const dayjsLocaleImportMap = { ["ar-TN"]: ["ar-tn", () => import("dayjs/locale/ar-tn")], ["zh-HK"]: ["zh-hk", () => import("dayjs/locale/zh-hk")], ["ko"]: ["ko", () => import("dayjs/locale/ko")], + ["tr"]: ["tr", () => import("dayjs/locale/tr")], } export const ns = ["app", "common", "lang", "settings", "shortcuts", "errors"] as const export const defaultNS = "app" as const diff --git a/apps/renderer/src/@types/default-resource.ts b/apps/renderer/src/@types/default-resource.ts index dfe39035f6..da0f599fb0 100644 --- a/apps/renderer/src/@types/default-resource.ts +++ b/apps/renderer/src/@types/default-resource.ts @@ -16,6 +16,7 @@ import common_ja from "../../../../locales/common/ja.json" import common_ko from "../../../../locales/common/ko.json" import common_pt from "../../../../locales/common/pt.json" import common_ru from "../../../../locales/common/ru.json" +import common_tr from "../../../../locales/common/tr.json" import common_zhCN from "../../../../locales/common/zh-CN.json" import common_zhHK from "../../../../locales/common/zh-HK.json" import common_zhTW from "../../../../locales/common/zh-TW.json" @@ -36,6 +37,7 @@ import lang_ja from "../../../../locales/lang/ja.json" import lang_ko from "../../../../locales/lang/ko.json" import lang_pt from "../../../../locales/lang/pt.json" import lang_ru from "../../../../locales/lang/ru.json" +import lang_tr from "../../../../locales/lang/tr.json" import lang_zhCN from "../../../../locales/lang/zh-CN.json" import lang_zhHK from "../../../../locales/lang/zh-HK.json" import lang_zhTW from "../../../../locales/lang/zh-TW.json" @@ -85,4 +87,5 @@ export const defaultResources = { "ar-KW": { lang: lang_arkw, common: common_arkw }, "ar-TN": { lang: lang_artn, common: common_artn }, ko: { lang: lang_ko, common: common_ko }, + tr: { lang: lang_tr, common: common_tr }, } diff --git a/locales/app/tr.json b/locales/app/tr.json new file mode 100644 index 0000000000..fca76847ed --- /dev/null +++ b/locales/app/tr.json @@ -0,0 +1,244 @@ +{ + "achievement.all_done": "Tüm tamamlandı!", + "achievement.first_claim_feed": "Besleme Sahibi", + "achievement.first_claim_feed_description": "Follow'da beslemenizi sahibi olun.", + "achievement.mint_more_power": "Sert çalışan bir oyuncu olun ve daha fazla kazanın.", + "ai_daily.title": "Öne Çıkan Haberler - {{title}}", + "ai_daily.tooltip.content": "Burada, zaman çizelgenizden ( - ) AI tarafından seçilen ve sizin için önemli olabilecek haberler bulunmaktadır.", + "ai_daily.tooltip.update_schedule": "Her gün saat 08:00 ve 20:00'de güncellenir.", + "app.copy_logo_svg": "Logo SVG'sini Kopyala", + "app.toggle_sidebar": "Kenar Çubuğunu Aç/Kapat", + "discover.any_url_or_keyword": "Herhangi bir URL veya Anahtar Kelime", + "discover.default_option": " (varsayılan)", + "discover.feed_description": "Bu beslemesinin açıklaması aşağıdaki gibidir ve ilgili bilgilerle parametre formunu doldurabilirsiniz.", + "discover.feed_maintainers": "Bu besleme RSSHub tarafından sağlanmaktadır, katkılarıyla", + "discover.import.click_to_upload": "OPML dosyasını yüklemek için tıklayın", + "discover.import.conflictItems": "Çakışan Öğeler", + "discover.import.noItems": "Öğe yok", + "discover.import.opml": "OPML dosyası", + "discover.import.parsedErrorItems": "Ayrıştırma Hatası Olan Öğeler", + "discover.import.result": " besleme başarıyla içe aktarıldı, zaten aboneliğiniz vardı ve içe aktarılamadı.", + "discover.import.successfulItems": "Başarılı Öğeler", + "discover.popular": "Popüler", + "discover.preview": "Önizleme", + "discover.rss_hub_route": "RSSHub Rotası", + "discover.rss_url": "RSS URL'si", + "discover.select_placeholder": "Seçin", + "discover.target.feeds": "Beslemeler", + "discover.target.label": "Arama hedefi", + "discover.target.lists": "Listeler", + "early_access": "Erken Erişim", + "entry_actions.copy_link": "Bağlantıyı kopyala", + "entry_actions.failed_to_save_to_eagle": "Eagle'a kaydetme başarısız oldu.", + "entry_actions.failed_to_save_to_instapaper": "Instapaper'a kaydetme başarısız oldu.", + "entry_actions.failed_to_save_to_readwise": "Readwise'a kaydetme başarısız oldu.", + "entry_actions.link_copied": "Bağlantı panoya kopyalandı.", + "entry_actions.mark_as_read": "Okundu olarak işaretle", + "entry_actions.mark_as_unread": "Okunmadı olarak işaretle", + "entry_actions.open_in_browser": "Tarayıcıda aç", + "entry_actions.save_media_to_eagle": "Medyayı Eagle'a kaydet", + "entry_actions.save_to_instapaper": "Instapaper'a kaydet", + "entry_actions.save_to_readwise": "Readwise'a kaydet", + "entry_actions.saved_to_eagle": "Eagle'a kaydedildi.", + "entry_actions.saved_to_instapaper": "Instapaper'a kaydedildi.", + "entry_actions.saved_to_readwise": "Readwise'a kaydedildi.", + "entry_actions.share": "Paylaş", + "entry_actions.star": "Yıldızla", + "entry_actions.starred": "Yıldızlandı.", + "entry_actions.tip": "Bahşiş", + "entry_actions.unstar": "Yıldızı kaldır", + "entry_actions.unstarred": "Yıldız kaldırıldı.", + "entry_column.filtered_content_tip": "Filtrelenmiş içeriğiniz gizlendi.", + "entry_column.filtered_content_tip_2": "Yukarıda gösterilen girişlere ek olarak, filtrelenmiş içerik de bulunmaktadır.", + "entry_column.refreshing": "Yeni girişler yenileniyor...", + "entry_content.ai_summary": "AI özeti", + "entry_content.fetching_content": "Orijinal içerik alınıyor ve işleniyor...", + "entry_content.header.play_tts": "TTS'i Oynat", + "entry_content.header.readability": "Okunabilirlik", + "entry_content.no_content": "Medya mevcut değil", + "entry_content.readability_notice": "Bu içerik Okunabilirlik tarafından sağlanmaktadır. Tipografik anomaliler bulursanız, lütfen orijinal içeriği görmek için kaynak siteye gidin.", + "entry_content.render_error": "Oluşturma hatası:", + "entry_content.report_issue": "Sorun bildir", + "entry_content.support_amount": "{{amount}} kişi bu beslemesinin yaratıcısını destekledi.", + "entry_content.support_creator": "Yaratıcıyı Destekle", + "entry_content.web_app_notice": "Web uygulaması bu içerik türünü desteklemiyor olabilir. Ancak masaüstü uygulamasını indirebilirsiniz.", + "entry_list.zero_unread": "Okunmamış Yok", + "entry_list_header.daily_report": "Günlük Rapor", + "entry_list_header.hide_no_image_items": "Resimsiz girişleri gizle.", + "entry_list_header.items": "öğe", + "entry_list_header.new_entries_available": "Yeni girişler mevcut", + "entry_list_header.refetch": "Yeniden getir", + "entry_list_header.refresh": "Yenile", + "entry_list_header.show_all": "Tümünü göster", + "entry_list_header.show_all_items": "Tüm giriş öğelerini göster", + "entry_list_header.show_unread_only": "Sadece okunmamışları göster", + "entry_list_header.switch_to_grid": "Izgara görünümüne geç", + "entry_list_header.switch_to_masonry": "Masonry görünümüne geç", + "entry_list_header.unread": "okunmamış", + "feed_claim_modal.choose_verification_method": "Doğrulama için üç yöntem arasından seçim yapabilirsiniz.", + "feed_claim_modal.claim_button": "Talep Et", + "feed_claim_modal.content_instructions": "Aşağıdaki içeriği kopyalayın ve en son RSS beslemesine gönderin.", + "feed_claim_modal.description_current": "Mevcut açıklama:", + "feed_claim_modal.description_instructions": "Aşağıdaki içeriği kopyalayın ve RSS beslemesinin alanına yapıştırın.", + "feed_claim_modal.failed_to_load": "Talep mesajı yüklenemedi", + "feed_claim_modal.rss_format_choice": "RSS oluşturucuları genellikle iki format sunar. Lütfen ihtiyacınıza göre aşağıdaki XML ve JSON formatlarını kopyalayın.", + "feed_claim_modal.rss_instructions": "Aşağıdaki kodu kopyalayın ve RSS oluşturucunuza yapıştırın.", + "feed_claim_modal.rss_json_format": "JSON Formatı", + "feed_claim_modal.rss_xml_format": "XML Formatı", + "feed_claim_modal.rsshub_notice": "Bu besleme, 1 saatlik önbellek süresiyle RSSHub tarafından sağlanmaktadır. İçerik yayınlandıktan sonra değişikliklerin görünmesi 1 saate kadar sürebilir.", + "feed_claim_modal.tab_content": "İçerik", + "feed_claim_modal.tab_description": "Açıklama", + "feed_claim_modal.tab_rss": "RSS Etiketi", + "feed_claim_modal.title": "Besleme Talep", + "feed_claim_modal.verify_ownership": "Bu beslemeyi kendinize ait olarak talep etmek için sahipliğinizi doğrulamanız gerekiyor.", + "feed_form.add_follow": "Takip ekle", + "feed_form.category": "Kategori", + "feed_form.category_description": "Varsayılan olarak, takipleriniz web sitesine göre gruplandırılacaktır.", + "feed_form.error_fetching_feed": "Besleme getirilirken hata oluştu.", + "feed_form.fee": "Takip ücreti", + "feed_form.fee_description": "Bu listenin takip edilmesi için listenin sahibine bir ücret ödenmelidir.", + "feed_form.feed_not_found": "Besleme bulunamadı.", + "feed_form.feedback": "Geri bildirim", + "feed_form.follow": "Takip et", + "feed_form.follow_with_fee": "{{fee}} Güç ile takip et", + "feed_form.followed": "🎉 Takip edildi.", + "feed_form.private_follow": "Özel Takip", + "feed_form.private_follow_description": "Bu takibin profil sayfanızda herkese açık olarak görünüp görünmeyeceği.", + "feed_form.retry": "Yeniden dene", + "feed_form.title": "Başlık", + "feed_form.title_description": "Bu Besleme için özel başlık. Varsayılanı kullanmak için boş bırakın.", + "feed_form.unfollow": "Takibi bırak", + "feed_form.update": "Güncelle", + "feed_form.update_follow": "Takibi güncelle", + "feed_form.updated": "🎉 Güncellendi.", + "feed_form.view": "Görüntüle", + "feed_item.claimed_by_owner": "Bu besleme şu kişi tarafından talep edildi:", + "feed_item.claimed_by_unknown": "sahibi.", + "feed_item.claimed_by_you": "Sizin tarafınızdan talep edildi", + "feed_item.claimed_feed": "Talep Edilen Besleme", + "feed_item.error_since": "Hata başlangıcı", + "feed_item.not_publicly_visible": "Profil sayfanızda herkese açık olarak görünmüyor", + "feed_view_type.articles": "Makaleler", + "feed_view_type.audios": "Sesler", + "feed_view_type.notifications": "Bildirimler", + "feed_view_type.pictures": "Resimler", + "feed_view_type.social_media": "Sosyal Medya", + "feed_view_type.videos": "Videolar", + "mark_all_read_button.auto_confirm_info": "3 saniye sonra otomatik olarak onaylanacak.", + "mark_all_read_button.confirm": "Onayla", + "mark_all_read_button.confirm_mark_all": "{{which}} okundu olarak işaretlensin mi?", + "mark_all_read_button.confirm_mark_all_info": "Tümünü okundu olarak işaretlemeyi onaylıyor musunuz?", + "mark_all_read_button.mark_all_as_read": "Tümünü okundu olarak işaretle", + "mark_all_read_button.mark_as_read": "{{which}} okundu olarak işaretle", + "mark_all_read_button.undo": "Geri al", + "notify.unfollow_feed": " takibi bırakıldı.", + "player.back_10s": "10 saniye geri", + "player.close": "Kapat", + "player.download": "İndir", + "player.exit_full_screen": "Tam Ekrandan Çık", + "player.forward_10s": "10 saniye ileri", + "player.full_screen": "Tam Ekran", + "player.mute": "Sessize Al", + "player.open_entry": "Girişi Aç", + "player.pause": "Duraklat", + "player.play": "Oynat", + "player.playback_rate": "Oynatma Hızı", + "player.unmute": "Sesi Aç", + "player.volume": "Ses", + "resize.tooltip.double_click_to_collapse": "Çift tıkla kapat", + "resize.tooltip.drag_to_resize": "Sürükle yeniden boyutlandır", + "search.empty.no_results": "Sonuç bulunamadı.", + "search.group.entries": "Girişler", + "search.group.feeds": "Beslemeler", + "search.options.all": "Tümü", + "search.options.entries": "Girişler", + "search.options.feeds": "Beslemeler", + "search.options.search_type": "Arama Türü", + "search.placeholder": "Ara...", + "search.result_count_local_mode": "(Yerel mod)", + "search.tooltip.local_search": "Bu arama yerel olarak mevcut verileri kapsar. En son verileri dahil etmek için Yeniden Getir'i deneyin.", + "shortcuts.guide.title": "Kısayollar Kılavuzu", + "sidebar.add_more_feeds": "Daha fazla besleme ekle", + "sidebar.category_remove_dialog.cancel": "İptal", + "sidebar.category_remove_dialog.continue": "Devam Et", + "sidebar.category_remove_dialog.description": "Bu işlem kategorinizi silecek, ancak içerdiği beslemeler korunacak ve web sitesine göre gruplandırılacaktır.", + "sidebar.category_remove_dialog.title": "Kategoriyi Kaldır", + "sidebar.feed_actions.claim": "Talep Et", + "sidebar.feed_actions.claim_feed": "Beslemeyi Talep Et", + "sidebar.feed_actions.copy_feed_id": "Besleme ID'sini kopyala", + "sidebar.feed_actions.copy_feed_url": "Besleme URL'sini kopyala", + "sidebar.feed_actions.copy_list_id": "Liste ID'sini kopyala", + "sidebar.feed_actions.copy_list_url": "Liste URL'sini kopyala", + "sidebar.feed_actions.edit": "Düzenle", + "sidebar.feed_actions.edit_feed": "Beslemeyi düzenle", + "sidebar.feed_actions.edit_list": "Listeyi düzenle", + "sidebar.feed_actions.feed_owned_by_you": "Bu besleme size ait", + "sidebar.feed_actions.list_owned_by_you": "Bu liste size ait", + "sidebar.feed_actions.mark_all_as_read": "Tümünü okundu olarak işaretle", + "sidebar.feed_actions.navigate_to_feed": "Beslemeye git", + "sidebar.feed_actions.navigate_to_list": "Listeye git", + "sidebar.feed_actions.open_feed_in_browser": "Beslemeyi tarayıcıda aç", + "sidebar.feed_actions.open_list_in_browser": "Listeyi tarayıcıda aç", + "sidebar.feed_actions.open_site_in_browser": "Siteyi tarayıcıda aç", + "sidebar.feed_actions.unfollow": "Takibi bırak", + "sidebar.feed_actions.unfollow_feed": "Besleme takibini bırak", + "sidebar.feed_column.context_menu.change_to_other_view": "Diğer görünüme geç", + "sidebar.feed_column.context_menu.delete_category": "Kategoriyi sil", + "sidebar.feed_column.context_menu.delete_category_confirmation": "{{folderName}} kategorisini silmek istiyor musunuz?", + "sidebar.feed_column.context_menu.mark_as_read": "Okundu olarak işaretle", + "sidebar.feed_column.context_menu.rename_category": "Kategoriyi yeniden adlandır", + "sidebar.select_sort_method": "Bir sıralama yöntemi seçin", + "signin.continue_with_github": "GitHub ile devam et", + "signin.continue_with_google": "Google ile devam et", + "signin.sign_in_to": "Giriş yap", + "sync_indicator.disabled": "Güvenlik nedeniyle senkronizasyon devre dışı bırakıldı.", + "sync_indicator.offline": "Çevrimdışı", + "sync_indicator.synced": "Sunucu ile senkronize edildi", + "tip_modal.amount": "Miktar", + "tip_modal.claim_feed": "Bu beslemeyi talep et", + "tip_modal.create_wallet": "Ücretsiz Oluştur", + "tip_modal.feed_owner": "Besleme Sahibi", + "tip_modal.low_balance": "Bakiyeniz bu bahşiş için yeterli değil. Lütfen miktarı ayarlayın.", + "tip_modal.no_wallet": "Henüz bir cüzdanınız yok. Bahşiş vermek için lütfen bir cüzdan oluşturun.", + "tip_modal.tip_amount_sent": "yazara gönderildi.", + "tip_modal.tip_now": "Şimdi Bahşiş Ver", + "tip_modal.tip_sent": "Bahşiş başarıyla gönderildi! Desteğiniz için teşekkür ederiz.", + "tip_modal.tip_support": "⭐ Desteğinizi göstermek için bahşiş verin!", + "tip_modal.unclaimed_feed": "Henüz kimse bu beslemeyi talep etmedi. Alınan Power, talep edilene kadar blok zinciri sözleşmesinde güvenli bir şekilde tutulacaktır.", + "user_button.account": "Hesap", + "user_button.achievement": "Başarılar", + "user_button.download_desktop_app": "Masaüstü uygulamasını indir", + "user_button.log_out": "Çıkış yap", + "user_button.power": "Güç", + "user_button.preferences": "Tercihler", + "user_button.profile": "Profil", + "user_profile.close": "Kapat", + "user_profile.edit": "Düzenle", + "user_profile.loading": "Yükleniyor", + "user_profile.share": "Paylaş", + "user_profile.toggle_item_style": "Öğe Stilini Değiştir", + "words.achievement": "Başarılar", + "words.add": "Ekle", + "words.confirm": "Onayla", + "words.discover": "Keşfet", + "words.email": "E-posta", + "words.feeds": "Beslemeler", + "words.import": "İçe Aktar", + "words.items": "Öğeler", + "words.language": "Dil", + "words.lists": "Listeler", + "words.load_archived_entries": "Arşivlenmiş girişleri yükle", + "words.login": "Giriş", + "words.mint": "Mint", + "words.power": "Güç", + "words.rss": "RSS", + "words.rss3": "RSS3", + "words.rsshub": "RSSHub", + "words.search": "Ara", + "words.starred": "Yıldızlı", + "words.undo": "Geri al", + "words.unread": "Okunmamış", + "words.user": "Kullanıcı", + "words.which.all": "tümü", + "words.zero_items": "Sıfır öğe" +} diff --git a/locales/common/tr.json b/locales/common/tr.json new file mode 100644 index 0000000000..8dbd4ffbbb --- /dev/null +++ b/locales/common/tr.json @@ -0,0 +1,34 @@ +{ + "app.copied_to_clipboard": "Panoya kopyalandı", + "cancel": "İptal", + "close": "Kapat", + "confirm": "Onayla", + "ok": "Tamam", + "quantifier.piece": "", + "time.last_night": "Dün Gece", + "time.the_night_before_last": "Evvelsi Gece", + "time.today": "Bugün", + "time.yesterday": "Dün", + "tips.load-lng-error": "Dil paketi yüklenemedi", + "words.actions": "İşlemler", + "words.back": "Geri", + "words.copy": "Kopyala", + "words.create": "Oluştur", + "words.edit": "Düzenle", + "words.entry": "Giriş", + "words.id": "ID", + "words.items_one": "Öğe", + "words.items_other": "Öğeler", + "words.local": "yerel", + "words.manage": "Yönet", + "words.record": "kayıt", + "words.record_one": "kayıt", + "words.record_other": "kayıtlar", + "words.result": "sonuç", + "words.result_one": "sonuç", + "words.result_other": "sonuçlar", + "words.space": " ", + "words.submit": "Gönder", + "words.update": "Güncelle", + "words.which.all": "Tümü" +} diff --git a/locales/errors/tr.json b/locales/errors/tr.json new file mode 100644 index 0000000000..b1e4f899c9 --- /dev/null +++ b/locales/errors/tr.json @@ -0,0 +1,29 @@ +{ + "1000": "Yetkisiz", + "1001": "Oturum oluşturma başarısız", + "1002": "Geçersiz parametre", + "1003": "Geçersiz davet", + "2000": "Yalnızca yöneticiler beslemeleri yenileyebilir", + "2001": "Besleme bulunamadı", + "2002": "feedId veya url gerekli", + "2003": "Besleme getirme hatası", + "2004": "Besleme ayrıştırılamadı", + "2010": "Sahiplik doğrulaması başarısız oldu", + "2011": "Abonelik limiti aşıldı", + "3000": "Giriş bulunamadı", + "3001": "Giriş kullanıcıya ait değil", + "4000": "Zaten talep edilmiş", + "4001": "Kullanıcı cüzdan hatası", + "4002": "Yetersiz bakiye", + "4003": "Beslemenin çekilebilir bakiyesi yetersiz", + "4004": "Hedef kullanıcı cüzdan hatası", + "5000": "Davet limiti aşıldı. Lütfen birkaç gün sonra tekrar deneyin.", + "5001": "Davet zaten mevcut.", + "5002": "Davet kodu zaten kullanılmış.", + "5003": "Davet kodu mevcut değil.", + "6000": "Kullanıcı bulunamadı", + "7000": "Ayar bulunamadı", + "7001": "Geçersiz ayar sekmesi", + "7002": "Geçersiz ayar yükü", + "7003": "Ayar yükü çok büyük" +} diff --git a/locales/external/tr.json b/locales/external/tr.json new file mode 100644 index 0000000000..f7fc56e1ef --- /dev/null +++ b/locales/external/tr.json @@ -0,0 +1,36 @@ +{ + "copied_link": "Bağlantı panoya kopyalandı", + "feed.feeds_one": "besleme", + "feed.feeds_other": "besleme", + "feed.follower_one": "takipçi", + "feed.follower_other": "takipçi", + "feed.follower_to_view_all": "takipçi", + "feed.followsAndFeeds": "{{subscriptionCount}} {{subscriptionNoun}} ve {{feedsCount}} {{feedsNoun}} {{appName}} üzerinde", + "feed.followsAndReads": "{{appName}} üzerinde {{readCount}} {{readNoun}} ile {{subscriptionCount}} {{subscriptionNoun}}", + "feed.read_one": "okuma", + "feed.read_other": "okuma", + "header.app": "Uygulama", + "header.download": "İndir", + "invitation.activate": "Etkinleştir", + "invitation.codeOptions.1": "Sizi davet edecek herhangi bir alfa test kullanıcısı aranıyor.", + "invitation.codeOptions.2": "Ara sıra yapılan çekilişler için Discord sunucumuza katılın.", + "invitation.codeOptions.3": "Ara sıra yapılan çekilişler için X hesabımızı takip edin.", + "invitation.earlyAccess": "Follow şu anda erken erişimde ve kullanmak için bir davet kodu gerektiriyor.", + "invitation.earlyAccessMessage": "😰 Üzgünüz, Follow şu anda erken erişimde ve kullanmak için bir davet kodu gerektiriyor.", + "invitation.generateButton": "Yeni kod oluştur", + "invitation.generateCost": "Arkadaşlarınız için bir davet kodu oluşturmak için {{INVITATION_PRICE}} Güç harcayabilirsiniz.", + "invitation.getCodeMessage": "Aşağıdaki yollarla bir davet kodu alabilirsiniz:", + "invitation.title": "Davet Kodu", + "login.backToWebApp": "Web Uygulamasına Geri Dön", + "login.continueWithGitHub": "GitHub ile devam et", + "login.continueWithGoogle": "Google ile devam et", + "login.logInTo": "Giriş yap ", + "login.openApp": "Uygulamayı Aç", + "login.redirecting": "Yönlendiriliyor", + "login.signOut": "Çıkış yap", + "login.welcomeTo": "Hoş geldiniz ", + "redirect.continueInBrowser": "Tarayıcıda Devam Et", + "redirect.instruction": "Şimdi {{APP_NAME}} uygulamasını açma ve bu sayfayı güvenle kapatma zamanı.", + "redirect.openApp": "{{APP_NAME}} Uygulamasını Aç", + "redirect.successMessage": "{{APP_NAME}} Hesabına başarıyla bağlandınız." +} diff --git a/locales/lang/ar-DZ.json b/locales/lang/ar-DZ.json index 0be67b1278..15f35f4690 100644 --- a/locales/lang/ar-DZ.json +++ b/locales/lang/ar-DZ.json @@ -14,6 +14,7 @@ "langs.ko": "الكورية", "langs.pt": "البرتغالية", "langs.ru": "الروسية", + "langs.tr": "التركية", "langs.zh-CN": "الصينية المبسطة", "langs.zh-HK": "الصينية التقليدية (هونغ كونغ)", "langs.zh-TW": "الصينية التقليدية (تايوان)", diff --git a/locales/lang/ar-IQ.json b/locales/lang/ar-IQ.json index e548b1c1eb..652d8a756f 100644 --- a/locales/lang/ar-IQ.json +++ b/locales/lang/ar-IQ.json @@ -14,6 +14,7 @@ "langs.ko": "الكورية", "langs.pt": "البرتغالية", "langs.ru": "الروسية", + "langs.tr": "التركية", "langs.zh-CN": "الصينية المبسطة", "langs.zh-HK": "الصينية التقليدية (هونغ كونغ)", "langs.zh-TW": "الصينية التقليدية (تايوان)", diff --git a/locales/lang/ar-KW.json b/locales/lang/ar-KW.json index 8665a12511..ceb5b1a41d 100644 --- a/locales/lang/ar-KW.json +++ b/locales/lang/ar-KW.json @@ -14,6 +14,7 @@ "langs.ko": "الكورية", "langs.pt": "البرتغالية", "langs.ru": "الروسية", + "langs.tr": "التركية", "langs.zh-CN": "الصينية المبسطة", "langs.zh-HK": "الصينية التقليدية (هونغ كونغ)", "langs.zh-TW": "الصينية التقليدية (تايوان)", diff --git a/locales/lang/ar-MA.json b/locales/lang/ar-MA.json index fd318c4ade..f4bdf41cb6 100644 --- a/locales/lang/ar-MA.json +++ b/locales/lang/ar-MA.json @@ -14,6 +14,7 @@ "langs.ko": "الكورية", "langs.pt": "البرتغالية", "langs.ru": "الروسية", + "langs.tr": "التركية", "langs.zh-CN": "الصينية المبسطة", "langs.zh-HK": "الصينية التقليدية (هونغ كونغ)", "langs.zh-TW": "الصينية التقليدية (تايوان)", diff --git a/locales/lang/ar-TN.json b/locales/lang/ar-TN.json index 32f1b2bd1b..6a58394cf8 100644 --- a/locales/lang/ar-TN.json +++ b/locales/lang/ar-TN.json @@ -14,6 +14,7 @@ "langs.ko": "الكورية", "langs.pt": "البرتغالية", "langs.ru": "الروسية", + "langs.tr": "التركية", "langs.zh-CN": "الصينية المبسطة", "langs.zh-HK": "الصينية التقليدية (هونغ كونغ)", "langs.zh-TW": "الصينية التقليدية (تايوان)", diff --git a/locales/lang/en.json b/locales/lang/en.json index b3d487c245..a4495991e8 100644 --- a/locales/lang/en.json +++ b/locales/lang/en.json @@ -14,6 +14,7 @@ "langs.ko": "Korean", "langs.pt": "Portuguese", "langs.ru": "Russian", + "langs.tr": "Turkish", "langs.zh-CN": "Simplified Chinese", "langs.zh-HK": "Traditional Chinese (Hong Kong)", "langs.zh-TW": "Traditional Chinese (Taiwan)", diff --git a/locales/lang/es.json b/locales/lang/es.json index 9c9555db94..24420b752e 100644 --- a/locales/lang/es.json +++ b/locales/lang/es.json @@ -14,6 +14,7 @@ "langs.ko": "Coreano", "langs.pt": "Portugués", "langs.ru": "Ruso", + "langs.tr": "Turco", "langs.zh-CN": "Chino simplificado", "langs.zh-HK": "Chino tradicional (Hong Kong)", "langs.zh-TW": "Chino tradicional (Taiwán)", diff --git a/locales/lang/fi.json b/locales/lang/fi.json index 07e5153644..d05e7da833 100644 --- a/locales/lang/fi.json +++ b/locales/lang/fi.json @@ -14,6 +14,7 @@ "langs.ko": "Kore", "langs.pt": "Portugali", "langs.ru": "Venäjä", + "langs.tr": "Turkki", "langs.zh-CN": "Yksinkertaistettu kiina", "langs.zh-HK": "Perinteinen kiina (Hongkong)", "langs.zh-TW": "Perinteinen kiina (Taiwan)", diff --git a/locales/lang/fr.json b/locales/lang/fr.json index 88826624b7..07bcd9e67f 100644 --- a/locales/lang/fr.json +++ b/locales/lang/fr.json @@ -14,6 +14,7 @@ "langs.ko": "Coréen", "langs.pt": "Portugais", "langs.ru": "Russe", + "langs.tr": "Turc", "langs.zh-CN": "Chinois simplifié", "langs.zh-HK": "Chinois traditionnel (Hong Kong)", "langs.zh-TW": "Chinois traditionnel (Taïwan)", diff --git a/locales/lang/it.json b/locales/lang/it.json index 93b139723e..c5307bc01b 100644 --- a/locales/lang/it.json +++ b/locales/lang/it.json @@ -14,6 +14,7 @@ "langs.ko": "Coreano", "langs.pt": "Portoghese", "langs.ru": "Russo", + "langs.tr": "Turco", "langs.zh-CN": "Cinese semplificato", "langs.zh-HK": "Cinese tradizionale (Hong Kong)", "langs.zh-TW": "Cinese tradizionale (Taiwan)", diff --git a/locales/lang/ja.json b/locales/lang/ja.json index 1fc5d29db7..288f4577f9 100644 --- a/locales/lang/ja.json +++ b/locales/lang/ja.json @@ -14,6 +14,7 @@ "langs.ko": "韓国語", "langs.pt": "ポルトガル語", "langs.ru": "ロシア語", + "langs.tr": "トルコ語", "langs.zh-CN": "簡体字中国語", "langs.zh-HK": "繁体字中国語(香港)", "langs.zh-TW": "繁体字中国語(台湾)", diff --git a/locales/lang/ko.json b/locales/lang/ko.json index e0b184431d..a648d776fb 100644 --- a/locales/lang/ko.json +++ b/locales/lang/ko.json @@ -14,6 +14,7 @@ "langs.ko": "한국어", "langs.pt": "포르투갈어", "langs.ru": "러시아어", + "langs.tr": "터키어", "langs.zh-CN": "간체 중국어", "langs.zh-HK": "번체 중국어 (홍콩)", "langs.zh-TW": "번체 중국어 (대만)", diff --git a/locales/lang/pt.json b/locales/lang/pt.json index 02806b008b..5bc119db00 100644 --- a/locales/lang/pt.json +++ b/locales/lang/pt.json @@ -14,6 +14,7 @@ "langs.ko": "Coreano", "langs.pt": "Português", "langs.ru": "Russo", + "langs.tr": "Turco", "langs.zh-CN": "Chinês simplificado", "langs.zh-HK": "Chinês tradicional (Hong Kong)", "langs.zh-TW": "Chinês tradicional (Taiwan)", diff --git a/locales/lang/ru.json b/locales/lang/ru.json index 439b0d4af1..e11f9e070b 100644 --- a/locales/lang/ru.json +++ b/locales/lang/ru.json @@ -14,6 +14,7 @@ "langs.ko": "Корейский", "langs.pt": "Португальский", "langs.ru": "Русский", + "langs.tr": "Турецкий", "langs.zh-CN": "Упрощенный китайский", "langs.zh-HK": "Традиционный китайский (Гонконг)", "langs.zh-TW": "Традиционный китайский (Тайвань)", diff --git a/locales/lang/tr.json b/locales/lang/tr.json new file mode 100644 index 0000000000..6a8bf4841d --- /dev/null +++ b/locales/lang/tr.json @@ -0,0 +1,22 @@ +{ + "langs.ar-DZ": "Arapça (Cezayir)", + "langs.ar-IQ": "Arapça (Irak)", + "langs.ar-KW": "Arapça (Kuveyt)", + "langs.ar-MA": "Arapça (Fas)", + "langs.ar-SA": "Arapça (Suudi Arabistan)", + "langs.ar-TN": "Arapça (Tunus)", + "langs.en": "İngilizce", + "langs.es": "İspanyolca", + "langs.fi": "Fince", + "langs.fr": "Fransızca", + "langs.it": "İtalyanca", + "langs.ja": "Japonca", + "langs.ko": "Korece", + "langs.pt": "Portekizce", + "langs.ru": "Rusça", + "langs.tr": "Türkçe", + "langs.zh-CN": "Basitleştirilmiş Çince", + "langs.zh-HK": "Geleneksel Çince (Hong Kong)", + "langs.zh-TW": "Geleneksel Çince (Tayvan)", + "name": "Türkçe" +} diff --git a/locales/lang/zh-CN.json b/locales/lang/zh-CN.json index e1af9571ac..fcd629207d 100644 --- a/locales/lang/zh-CN.json +++ b/locales/lang/zh-CN.json @@ -14,6 +14,7 @@ "langs.ko": "韩语", "langs.pt": "葡萄牙语", "langs.ru": "俄语", + "langs.tr": "土耳其语", "langs.zh-CN": "简体中文", "langs.zh-HK": "繁体中文(香港)", "langs.zh-TW": "繁体中文(台湾)", diff --git a/locales/lang/zh-HK.json b/locales/lang/zh-HK.json index 203120a7d8..d8447c4b57 100644 --- a/locales/lang/zh-HK.json +++ b/locales/lang/zh-HK.json @@ -14,6 +14,7 @@ "langs.ko": "韓語", "langs.pt": "葡萄牙語", "langs.ru": "俄語", + "langs.tr": "土耳其語", "langs.zh-CN": "簡體中文", "langs.zh-HK": "繁體中文(香港)", "langs.zh-TW": "繁體中文(台灣)", diff --git a/locales/lang/zh-TW.json b/locales/lang/zh-TW.json index 97a30fbe99..aeb7cc9eb3 100644 --- a/locales/lang/zh-TW.json +++ b/locales/lang/zh-TW.json @@ -14,6 +14,7 @@ "langs.ko": "韓語", "langs.pt": "葡萄牙語", "langs.ru": "俄語", + "langs.tr": "土耳其語", "langs.zh-CN": "簡體中文", "langs.zh-HK": "繁體中文(香港)", "langs.zh-TW": "繁體中文(台灣)", diff --git a/locales/native/tr.json b/locales/native/tr.json new file mode 100644 index 0000000000..c7cb63a67e --- /dev/null +++ b/locales/native/tr.json @@ -0,0 +1,68 @@ +{ + "contextMenu.copy": "Kopyala", + "contextMenu.copyImage": "Resmi Kopyala", + "contextMenu.copyImageAddress": "Resim Adresini Kopyala", + "contextMenu.copyLink": "Bağlantıyı Kopyala", + "contextMenu.copyVideoAddress": "Video Adresini Kopyala", + "contextMenu.cut": "Kes", + "contextMenu.inspect": "Öğeyi İncele", + "contextMenu.learnSpelling": "Yazımı Öğren", + "contextMenu.lookUpSelection": "Seçimi Ara", + "contextMenu.openImageInBrowser": "Resmi Tarayıcıda Aç", + "contextMenu.openLinkInBrowser": "Bağlantıyı Tarayıcıda Aç", + "contextMenu.paste": "Yapıştır", + "contextMenu.saveImage": "Resmi Kaydet", + "contextMenu.saveImageAs": "Resmi Farklı Kaydet...", + "contextMenu.saveLinkAs": "Bağlantıyı Farklı Kaydet...", + "contextMenu.saveVideo": "Videoyu Kaydet", + "contextMenu.saveVideoAs": "Videoyu Farklı Kaydet...", + "contextMenu.searchWithGoogle": "Google ile Ara", + "contextMenu.selectAll": "Tümünü Seç", + "contextMenu.services": "Hizmetler", + "dialog.clearAllData": "Tüm verileri temizlemek istediğinize emin misiniz?", + "dialog.no": "Hayır", + "dialog.yes": "Evet", + "menu.about": "{{name}} Hakkında", + "menu.actualSize": "Gerçek Boyut", + "menu.bringAllToFront": "Tümünü Öne Getir", + "menu.checkForUpdates": "Güncellemeleri Kontrol Et", + "menu.clearAllData": "Tüm verileri temizle", + "menu.close": "Kapat", + "menu.copy": "Kopyala", + "menu.cut": "Kes", + "menu.debug": "Hata Ayıklama", + "menu.delete": "Sil", + "menu.discover": "Keşfet", + "menu.edit": "Düzenle", + "menu.file": "Dosya", + "menu.followReleases": "Sürümleri takip et", + "menu.forceReload": "Zorla Yenile", + "menu.front": "Öne Getir", + "menu.help": "Yardım", + "menu.hide": "{{name}} Uygulamasını Gizle", + "menu.hideOthers": "Diğerlerini Gizle", + "menu.minimize": "Simge Durumuna Küçült", + "menu.openLogFile": "Günlük dosyasını aç", + "menu.paste": "Yapıştır", + "menu.pasteAndMatchStyle": "Yapıştır ve Stili Eşleştir", + "menu.quickAdd": "Hızlı Ekle", + "menu.quit": "{{name}} Uygulamasından Çık", + "menu.quitAndInstallUpdate": "Hata Ayıklama: Çık ve Güncellemeyi Yükle", + "menu.redo": "Yinele", + "menu.reload": "Yenile", + "menu.search": "Ara", + "menu.selectAll": "Tümünü Seç", + "menu.services": "Hizmetler", + "menu.settings": "Ayarlar...", + "menu.speech": "Konuşma", + "menu.startSpeaking": "Konuşmaya Başla", + "menu.stopSpeaking": "Konuşmayı Durdur", + "menu.toggleDevTools": "Geliştirici Araçlarını Aç/Kapat", + "menu.toggleFullScreen": "Tam Ekranı Aç/Kapat", + "menu.undo": "Geri Al", + "menu.view": "Görünüm", + "menu.window": "Pencere", + "menu.zoom": "Yakınlaştır", + "menu.zoomIn": "Yakınlaştır", + "menu.zoomOut": "Uzaklaştır" +} diff --git a/locales/settings/tr.json b/locales/settings/tr.json new file mode 100644 index 0000000000..9f05b4a87f --- /dev/null +++ b/locales/settings/tr.json @@ -0,0 +1,236 @@ +{ + "about.changelog": "Değişiklik Günlüğü", + "about.feedbackInfo": "{{appName}} ({{commitSha}}) geliştirmenin erken aşamalarındadır. Herhangi bir geri bildiriminiz veya öneriniz varsa, lütfen GitHub'ımızda bir konu açmaktan çekinmeyin.", + "about.iconLibrary": "Kullanılan simge kütüphanesi tarafından telif hakkıyla korunmaktadır ve yeniden dağıtılamaz.", + "about.licenseInfo": "{{appName}} her zaman ücretsiz ve açık kaynaklı bir proje olacaktır. {{license}} lisansı altında lisanslanmıştır.", + "about.sidebar_title": "Hakkında", + "about.socialMedia": "Sosyal Medya", + "actions.actionName": "Eylem {{number}}", + "actions.action_card.add": "Ekle", + "actions.action_card.all": "Tümü", + "actions.action_card.block_rules": "Engelleme Kuralları", + "actions.action_card.custom_filters": "Özel filtreler", + "actions.action_card.enable_readability": "Okunabilirliği etkinleştir", + "actions.action_card.entry_options.all": "Tümü", + "actions.action_card.entry_options.author": "Yazar", + "actions.action_card.entry_options.content": "İçerik", + "actions.action_card.entry_options.order": "Sıralama", + "actions.action_card.entry_options.title": "Başlık", + "actions.action_card.entry_options.url": "URL", + "actions.action_card.feed_options.category": "Kategori", + "actions.action_card.feed_options.feed_url": "Besleme URL'si", + "actions.action_card.feed_options.site_url": "Site URL'si", + "actions.action_card.feed_options.title": "Başlık", + "actions.action_card.feed_options.view": "Görünüm", + "actions.action_card.field": "Alan", + "actions.action_card.from": "Kimden", + "actions.action_card.generate_summary": "Yapay zeka kullanarak özet oluştur", + "actions.action_card.name": "İsim", + "actions.action_card.operation_options.contains": "içerir", + "actions.action_card.operation_options.does_not_contain": "içermez", + "actions.action_card.operation_options.is_equal_to": "eşittir", + "actions.action_card.operation_options.is_greater_than": "büyüktür", + "actions.action_card.operation_options.is_less_than": "küçüktür", + "actions.action_card.operation_options.is_not_equal_to": "eşit değildir", + "actions.action_card.operation_options.matches_regex": "regex ile eşleşir", + "actions.action_card.operator": "Operatör", + "actions.action_card.rewrite_rules": "Yeniden Yazma Kuralları", + "actions.action_card.then_do": "Sonra yap…", + "actions.action_card.to": "Kime", + "actions.action_card.translate_into": "Şu dile çevir", + "actions.action_card.value": "Değer", + "actions.action_card.when_feeds_match": "Beslemeler eşleştiğinde…", + "actions.newRule": "Yeni Kural", + "actions.save": "Kaydet", + "actions.saveSuccess": "🎉 Eylemler kaydedildi.", + "actions.sidebar_title": "Eylemler", + "actions.title": "Eylemler", + "appearance.code_highlight_theme": "Kod vurgulama teması", + "appearance.content": "İçerik", + "appearance.content_font": "İçerik Yazı Tipi", + "appearance.custom_font": "Özel Yazı Tipi", + "appearance.fonts": "Yazı Tipleri", + "appearance.general": "Genel", + "appearance.guess_code_language.description": "Etiketlenmemiş kod bloklarını çıkarmak için modeller kullanan başlıca programlama dilleri", + "appearance.guess_code_language.label": "Kod dilini tahmin et", + "appearance.misc": "Çeşitli", + "appearance.modal_overlay.description": "Modal arka planını göster", + "appearance.modal_overlay.label": "Modal arka planını göster", + "appearance.opaque_sidebars.label": "Opak kenar çubukları", + "appearance.reader_render_inline_style.description": "Orijinal HTML'nin satır içi stilinin işlenmesine izin verir.", + "appearance.reader_render_inline_style.label": "Satır içi stili işle", + "appearance.reduce_motion.description": "Performansı artırmak ve enerji tüketimini azaltmak için öğelerin hareketini azaltma", + "appearance.reduce_motion.label": "Hareketi azalt", + "appearance.save": "Kaydet", + "appearance.show_dock_badge.label": "Dock rozeti olarak göster", + "appearance.sidebar_show_unread_count.label": "Kenar çubuğunda göster", + "appearance.sidebar_title": "Görünüm", + "appearance.text_size": "Metin boyutu", + "appearance.theme.dark": "Koyu", + "appearance.theme.label": "Tema", + "appearance.theme.light": "Açık", + "appearance.theme.system": "Sistem", + "appearance.title": "Görünüm", + "appearance.ui_font": "Arayüz Yazı Tipi", + "appearance.unread_count": "Okunmamış sayısı", + "appearance.use_pointer_cursor.description": "Herhangi bir etkileşimli öğenin üzerine gelindiğinde imleci bir işaretçiye dönüştürün.", + "appearance.use_pointer_cursor.label": "İşaretçi imlecini kullan", + "common.give_star": "Ürünümüzü beğendiniz mi? GitHub'da bize bir yıldız verin!", + "feeds.claimTips": "Beslemelerinizi talep etmek ve bahşiş almak için, abonelik listenizde beslemeye sağ tıklayın ve Talep Et'i seçin.", + "feeds.noFeeds": "Talep edilmiş besleme yok", + "feeds.tableHeaders.entryCount": "Girdiler", + "feeds.tableHeaders.name": "İsim", + "feeds.tableHeaders.subscriptionCount": "Abonelikler", + "feeds.tableHeaders.tipAmount": "Alınan bahşişler", + "general.app": "Uygulama", + "general.data_persist.description": "Çevrimdışı erişimi ve yerel aramayı etkinleştirmek için verileri yerel olarak sakla.", + "general.data_persist.label": "Çevrimdışı kullanım için verileri sakla", + "general.group_by_date.description": "Girdileri tarihe göre grupla.", + "general.group_by_date.label": "Tarihe göre grupla", + "general.language": "Dil", + "general.launch_at_login": "Oturum açıldığında başlat", + "general.mark_as_read.hover.description": "Üzerine gelindiğinde girdileri otomatik olarak okundu olarak işaretle.", + "general.mark_as_read.hover.label": "Üzerine gelindiğinde okundu olarak işaretle", + "general.mark_as_read.render.description": "Tek seviyeli girdileri (örn. sosyal medya gönderileri, resimler, video görüntülemeleri) görünüme girdiklerinde otomatik olarak okundu olarak işaretle.", + "general.mark_as_read.render.label": "Görünümde olduğunda okundu olarak işaretle", + "general.mark_as_read.scroll.description": "Görünümden kaydırıldığında girdileri otomatik olarak okundu olarak işaretle.", + "general.mark_as_read.scroll.label": "Kaydırıldığında okundu olarak işaretle", + "general.network": "Ağ", + "general.privacy_data": "Gizlilik ve Veri", + "general.proxy.description": "Ağ trafiği yönlendirmesi için proxy ayarla, örn., socks://proxy.example.com:1080", + "general.proxy.label": "Proxy", + "general.rebuild_database.button": "Yeniden Oluştur", + "general.rebuild_database.description": "Görüntüleme sorunları yaşıyorsanız, veritabanını yeniden oluşturmak bu sorunları çözebilir.", + "general.rebuild_database.label": "Veritabanını Yeniden Oluştur", + "general.rebuild_database.title": "Veritabanını Yeniden Oluştur", + "general.rebuild_database.warning.line1": "Veritabanını yeniden oluşturmak tüm yerel verilerinizi temizleyecektir.", + "general.rebuild_database.warning.line2": "Devam etmek istediğinizden emin misiniz?", + "general.send_anonymous_data.description": "Anonim telemetri verisi göndermeyi seçerek, Follow'un genel kullanıcı deneyimini iyileştirmeye katkıda bulunursunuz.", + "general.send_anonymous_data.label": "Anonim veri gönder", + "general.show_unread_on_launch.description": "Başlangıçta okunmamış içeriği göster", + "general.show_unread_on_launch.label": "Başlangıçta okunmamış içeriği göster", + "general.sidebar_title": "Genel", + "general.timeline": "Zaman Çizelgesi", + "general.unread": "Okunmamış", + "general.voices": "Sesler", + "integration.eagle.enable.description": "Mevcut olduğunda 'Medyayı Eagle'a kaydet' düğmesini göster.", + "integration.eagle.enable.label": "Etkinleştir", + "integration.eagle.title": "Eagle", + "integration.instapaper.enable.description": "Mevcut olduğunda 'Instapaper'a kaydet' düğmesini göster.", + "integration.instapaper.enable.label": "Etkinleştir", + "integration.instapaper.password.label": "Instapaper Şifresi", + "integration.instapaper.title": "Instapaper", + "integration.instapaper.username.label": "Instapaper Kullanıcı Adı", + "integration.readwise.enable.description": "Mevcut olduğunda 'Readwise'a kaydet' düğmesini göster.", + "integration.readwise.enable.label": "Etkinleştir", + "integration.readwise.title": "Readwise", + "integration.readwise.token.description": "Buradan alabilirsiniz: readwise.io/access_token.", + "integration.readwise.token.label": "Readwise Erişim Tokeni", + "integration.sidebar_title": "Entegrasyon", + "integration.tip": "İpucu: Hassas verileriniz yerel olarak saklanır ve sunucuya yüklenmez.", + "integration.title": "Entegrasyon", + "invitation.activate": "Etkinleştir", + "invitation.codeOptions.betaUser": "1. Sizi davet eden bir beta kullanıcısı bulun.", + "invitation.codeOptions.discord": "2. Discord sunucumuza katılın ve zaman zaman hediyeler alın.", + "invitation.codeOptions.xAccount": "3. X hesabımızı takip edin ve zaman zaman hediyeler alın.", + "invitation.confirmModal.cancel": "İptal", + "invitation.confirmModal.confirm": "Devam etmek istiyor musunuz?", + "invitation.confirmModal.continue": "Devam", + "invitation.confirmModal.message": "Bir davet kodu oluşturmak size {{INVITATION_PRICE}} Power'a mal olacak.", + "invitation.confirmModal.title": "Onayla", + "invitation.earlyAccess": "Follow şu anda erken erişim aşamasındadır ve kullanmak için bir davet kodu gerektirir.", + "invitation.earlyAccessMessage": "😰 Üzgünüz, Follow şu anda erken erişim aşamasındadır ve kullanmak için bir davet kodu gerektirir.", + "invitation.generateButton": "Yeni kod oluştur", + "invitation.generateCost": "Arkadaşlarınız için bir davet kodu oluşturmak için {{INVITATION_PRICE}} Power harcayabilirsiniz.", + "invitation.getCodeMessage": "Aşağıdaki yöntemlerle bir davet kodu alabilirsiniz:", + "invitation.limitationMessage": "Kullanım sürenize bağlı olarak, en fazla {{limitation}} davet kodu oluşturabilirsiniz.", + "invitation.newInvitationSuccess": "🎉 Yeni davet oluşturuldu, davet kodu kopyalandı", + "invitation.noInvitations": "Davet yok", + "invitation.notUsed": "Kullanılmadı", + "invitation.sidebar_title": "Davetler", + "invitation.tableHeaders.code": "Kod", + "invitation.tableHeaders.creationTime": "Oluşturma Zamanı", + "invitation.tableHeaders.usedBy": "Kullanan", + "invitation.title": "Davet Kodu", + "lists.create": "Yeni Liste Oluştur", + "lists.created.error": "Liste oluşturulamadı.", + "lists.created.success": "Liste başarıyla oluşturuldu!", + "lists.description": "Açıklama", + "lists.edit.error": "Liste düzenlenemedi.", + "lists.edit.label": "Düzenle", + "lists.edit.success": "Liste başarıyla düzenlendi!", + "lists.fee.description": "Bu listenin üyeliği için diğerlerinin ödemesi gereken ücret.", + "lists.fee.label": "Ücret", + "lists.feeds.actions": "Eylemler", + "lists.feeds.add.error": "Besleme listesine eklenemedi.", + "lists.feeds.add.label": "Ekle", + "lists.feeds.add.success": "Besleme listesine başarıyla eklendi.", + "lists.feeds.delete.error": "Besleme listesinden kaldırılamadı.", + "lists.feeds.delete.success": "Besleme listesinden başarıyla kaldırıldı.", + "lists.feeds.id": "Besleme ID'si", + "lists.feeds.label": "Beslemeler", + "lists.feeds.manage": "Beslemeleri Yönet", + "lists.feeds.owner": "Sahibi", + "lists.feeds.search": "Besleme Ara", + "lists.feeds.title": "Başlık", + "lists.image": "Resim", + "lists.info": "Listeniz, diğerlerinin abone olabileceği beslemelerin bir koleksiyonudur. Aboneler, liste içindeki tüm beslemeleri senkronize eder ve erişir.", + "lists.noLists": "Liste yok", + "lists.submit": "Gönder", + "lists.title": "Başlık", + "lists.view": "Görüntüle", + "profile.avatar.label": "Avatar", + "profile.handle.description": "Benzersiz tanımlayıcınız.", + "profile.handle.label": "Kullanıcı Adı", + "profile.name.description": "Herkese açık görünen adınız.", + "profile.name.label": "Görünen Ad", + "profile.sidebar_title": "Profil", + "profile.submit": "Gönder", + "profile.title": "Profil Ayarları", + "profile.updateSuccess": "Profil güncellendi.", + "titles.about": "Hakkında", + "titles.actions": "Eylemler", + "titles.appearance": "Görünüm", + "titles.feeds": "Beslemeler", + "titles.general": "Genel", + "titles.integration": "Entegrasyon", + "titles.invitations": "Davetler", + "titles.power": "Power", + "titles.profile": "Profil", + "titles.shortcuts": "Kısayollar", + "wallet.address.title": "Adresiniz", + "wallet.balance.title": "Bakiyeniz", + "wallet.balance.withdrawable": "Çekilebilir", + "wallet.balance.withdrawableTooltip": "Çekilebilir Power, aldığınız bahşişleri ve yeniden yüklediğiniz Power'ı içerir.", + "wallet.claim.button.claim": "Günlük Power'ı Al", + "wallet.claim.button.claimed": "Bugün alındı", + "wallet.claim.tooltip.alreadyClaimed": "Bugün zaten aldınız.", + "wallet.claim.tooltip.canClaim": "{{amount}} Günlük Power'ınızı şimdi alın!", + "wallet.create.button": "Cüzdan Oluştur", + "wallet.create.description": "İçerik katkılarınız için ödüllendirilmek ve ayrıca içerik oluşturucuları ödüllendirmek için kullanılabilecek Power almak için ücretsiz bir cüzdan oluşturun.", + "wallet.power.dailyClaim": "Günlük olarak {{amount}} ücretsiz Power alabilirsiniz, bu Follow'da RSS girdilerine bahşiş vermek için kullanılabilir.", + "wallet.power.description": "Power, {{blockchainName}} blok zincirinde bir ERC-20 token'dır.", + "wallet.sidebar_title": "Power", + "wallet.transactions.amount": "Miktar", + "wallet.transactions.date": "Tarih", + "wallet.transactions.from": "Gönderen", + "wallet.transactions.noTransactions": "İşlem yok", + "wallet.transactions.title": "İşlemler", + "wallet.transactions.to": "Alıcı", + "wallet.transactions.tx": "İşlem", + "wallet.transactions.type": "Tür", + "wallet.transactions.types.burn": "yakma", + "wallet.transactions.types.mint": "basma", + "wallet.transactions.types.purchase": "satın alma", + "wallet.transactions.types.tip": "bahşiş", + "wallet.transactions.types.withdraw": "çekme", + "wallet.transactions.you": "Siz", + "wallet.withdraw.addressLabel": "Ethereum Adresiniz", + "wallet.withdraw.amountLabel": "Miktar", + "wallet.withdraw.availableBalance": "Cüzdanınızda çekilebilir Power var.", + "wallet.withdraw.button": "Çek", + "wallet.withdraw.error": "Çekme işlemi başarısız oldu: {{error}}", + "wallet.withdraw.modalTitle": "Power Çek", + "wallet.withdraw.submitButton": "Gönder", + "wallet.withdraw.success": "Çekme işlemi başarılı!" +} diff --git a/locales/shortcuts/tr.json b/locales/shortcuts/tr.json new file mode 100644 index 0000000000..5a104e6479 --- /dev/null +++ b/locales/shortcuts/tr.json @@ -0,0 +1,24 @@ +{ + "keys.audio.playPause": "Oynat/Duraklat (Ses oynatıcı açıkken)", + "keys.entries.markAllAsRead": "Tümünü Okundu Olarak İşaretle", + "keys.entries.next": "Sonraki Girdi", + "keys.entries.previous": "Önceki Girdi", + "keys.entries.refetch": "Yeniden Getir", + "keys.entries.toggleUnreadOnly": "Sadece Okunmamışları Göster/Gizle", + "keys.entry.copyLink": "Bağlantıyı Kopyala", + "keys.entry.openInBrowser": "Tarayıcıda Aç", + "keys.entry.openInNewTab": "Yeni Sekmede Aç", + "keys.entry.scrollDown": "Aşağı Kaydır", + "keys.entry.scrollUp": "Yukarı Kaydır", + "keys.entry.share": "Paylaş", + "keys.entry.tip": "Bahşiş Gücü", + "keys.entry.toggleRead": "Okundu/Okunmadı Olarak İşaretle", + "keys.entry.toggleStarred": "Yıldızlı/Yıldızsız Olarak İşaretle", + "keys.entry.tts": "Metinden Sese Oynat", + "keys.feeds.add": "Abonelik Ekle", + "keys.feeds.switchBetweenViews": "Görünümler Arası Geçiş", + "keys.feeds.switchToView": "Görünüme Geç", + "keys.layout.showShortcuts": "Kısayolları Göster/Gizle", + "keys.layout.toggleSidebar": "Besleme Kenar Çubuğunu Göster/Gizle", + "sidebar_title": "Kısayollar" +} From c63862d02f78d094f512fde2f8d4a21e723703ad Mon Sep 17 00:00:00 2001 From: Innei Date: Tue, 24 Sep 2024 21:43:10 +0800 Subject: [PATCH 08/10] chore(ci): add pr check Signed-off-by: Innei --- .github/workflows/pr-title-check.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .github/workflows/pr-title-check.yml diff --git a/.github/workflows/pr-title-check.yml b/.github/workflows/pr-title-check.yml new file mode 100644 index 0000000000..ed742d5033 --- /dev/null +++ b/.github/workflows/pr-title-check.yml @@ -0,0 +1,15 @@ +name: PR Conventional Commit Validation + +on: + pull_request: + types: [opened, synchronize, reopened, edited] + +jobs: + validate-pr-title: + runs-on: ubuntu-latest + steps: + - name: PR Conventional Commit Validation + uses: ytanikin/PRConventionalCommits@1.1.0 + with: + task_types: '["feat","fix","docs","test","ci","refactor","perf","chore","revert"]' + custom_labels: '{"feat": "feature", "fix": "fix", "docs": "documentation", "test": "test", "ci": "CI/CD", "refactor": "refactor", "perf": "performance", "chore": "chore", "revert": "revert"}' From 21b278772cc609957573b376cd6b66046f02aa64 Mon Sep 17 00:00:00 2001 From: 0x24a <96947247+0x24a@users.noreply.github.com> Date: Tue, 24 Sep 2024 23:32:33 +0800 Subject: [PATCH 09/10] chore(i18n): zh-CN translation (#613) --- locales/app/zh-CN.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/app/zh-CN.json b/locales/app/zh-CN.json index 546a837ace..675f40a963 100644 --- a/locales/app/zh-CN.json +++ b/locales/app/zh-CN.json @@ -2,7 +2,7 @@ "achievement.all_done": "大功告成!", "achievement.first_claim_feed": "订阅源所有者", "achievement.first_claim_feed_description": "在 Follow 上认证订阅源", - "achievement.mint_more_power": "成为核心玩家,赚取更多 ", + "achievement.mint_more_power": "成为硬核玩家,赚取更多 ", "ai_daily.title": "热点 - {{title}}", "ai_daily.tooltip.content": "AI 根据时间线( - )提取的重点信息", "ai_daily.tooltip.update_schedule": "每天 8:00 和 20:00 定时更新", From a45071b75da3fa343d52d3ddd6626693b73ea35d Mon Sep 17 00:00:00 2001 From: DIYgod Date: Wed, 25 Sep 2024 01:43:39 +0800 Subject: [PATCH 10/10] feat: update discord link --- README.md | 8 +++++--- apps/renderer/src/constants/social.ts | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e5d914656d..2f01e493fc 100644 --- a/README.md +++ b/README.md @@ -9,14 +9,16 @@ Next generation information browser.

- Discord + Discord · Twitter · Releases

- RSSNext%2Ffollow | Trendshift + RSSNext%2Ffollow | Trendshift + RSSNext%2Ffollow | Trendshift + RSSNext%2Ffollow | Trendshift

@@ -73,7 +75,7 @@ The project is currently under active development. The following screenshots are ## Releases -[![Discord](https://img.shields.io/discord/1243823539426033696?logo=discord&label=Discord&style=flat-square&color=5865F2)](https://discord.gg/xHCVQZ6wmZ) [![](https://img.shields.io/badge/any_text-Follow-blue?color=2CA5E0&label=_&logo=x&cacheSeconds=3600&style=flat-square)](https://x.com/intent/follow?screen_name=follow_app_) +[![Discord](https://img.shields.io/discord/1243823539426033696?logo=discord&label=Discord&style=flat-square&color=5865F2)](https://discord.gg/followapp) [![](https://img.shields.io/badge/any_text-Follow-blue?color=2CA5E0&label=_&logo=x&cacheSeconds=3600&style=flat-square)](https://x.com/intent/follow?screen_name=follow_app_) Currently, Follow is still in the early developer preview stage (alpha) and is only available to a limited number of users through an invitation system. diff --git a/apps/renderer/src/constants/social.ts b/apps/renderer/src/constants/social.ts index 2e4d8928ff..b3fb572d3c 100644 --- a/apps/renderer/src/constants/social.ts +++ b/apps/renderer/src/constants/social.ts @@ -9,7 +9,7 @@ export const SocialMediaLinks = [ { icon: "i-mgc-discord-cute-fi", label: "Discord", - url: "https://discord.gg/xHCVQZ6wmZ", + url: "https://discord.gg/followapp", }, { icon: "i-mgc-social-x-cute-re",