Skip to content

Commit

Permalink
feat: language indicator
Browse files Browse the repository at this point in the history
Signed-off-by: Innei <i@innei.in>
  • Loading branch information
Innei committed Sep 13, 2024
1 parent d81c2b5 commit bf7da04
Show file tree
Hide file tree
Showing 17 changed files with 136 additions and 83 deletions.
60 changes: 60 additions & 0 deletions configs/i18n-completeness.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import fs from "node:fs"
import path from "node:path"

type LanguageCompletion = Record<string, number>

function getLanguageFiles(dir: string): string[] {
return fs.readdirSync(dir).filter((file) => file.endsWith(".json"))
}

function getNamespaces(localesDir: string): string[] {
return fs
.readdirSync(localesDir)
.filter((file) => fs.statSync(path.join(localesDir, file)).isDirectory())
}

function countKeys(obj: any): number {
let count = 0
for (const key in obj) {
if (typeof obj[key] === "object") {
count += countKeys(obj[key])
} else {
count++
}
}
return count
}

function calculateCompleteness(localesDir: string): LanguageCompletion {
const namespaces = getNamespaces(localesDir)
const languages = new Set<string>()
const keyCount: Record<string, number> = {}

namespaces.forEach((namespace) => {
const namespaceDir = path.join(localesDir, namespace)
const files = getLanguageFiles(namespaceDir)

files.forEach((file) => {
const lang = path.basename(file, ".json")
languages.add(lang)

const content = JSON.parse(fs.readFileSync(path.join(namespaceDir, file), "utf-8"))
keyCount[lang] = (keyCount[lang] || 0) + countKeys(content)
})
})

const enCount = keyCount["en"] || 0
const completeness: LanguageCompletion = {}

languages.forEach((lang) => {
if (lang !== "en") {
const percent = Math.round((keyCount[lang] / enCount) * 100)
completeness[lang] = percent
}
})

return completeness
}

const i18n = calculateCompleteness(path.resolve(__dirname, "../locales"))
export default i18n
3 changes: 3 additions & 0 deletions configs/vite.render.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { prerelease } from "semver"
import type { Plugin, UserConfig } from "vite"

import { getGitHash } from "../scripts/lib"
import i18nCompleteness from "./i18n-completeness"

const pkg = JSON.parse(readFileSync("package.json", "utf8"))
const isCI = process.env.CI === "true" || process.env.CI === "1"
Expand Down Expand Up @@ -134,6 +135,8 @@ export const viteRenderBaseConfig = {
RELEASE_CHANNEL: JSON.stringify((prerelease(pkg.version)?.[0] as string) || "stable"),

DEBUG: process.env.DEBUG === "true",

I18N_COMPLETENESS_MAP: JSON.stringify({ ...i18nCompleteness, en: 100 }),
},
} satisfies UserConfig

Expand Down
3 changes: 3 additions & 0 deletions locales/app/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
"ai_daily.title": "Top News - {{title}}",
"ai_daily.tooltip.content": "Here is news selected by AI from your timeline (<From /> - <To />) that may be important to you.",
"ai_daily.tooltip.update_schedule": "Update daily at 8 AM and 8 PM.",
"app.app_name": "APP_NAME",
"app.copy_logo_svg": "Copy Logo SVG",
"app.toggle_sidebar": "Toggle Sidebar",
"discover.any_url_or_keyword": "Any URL or Keyword",
"discover.default_option": " (default)",
"discover.feed_description": "The description of this feed is as follows, and you can fill out the parameter form with the relevant information.",
Expand Down
3 changes: 3 additions & 0 deletions locales/app/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
"ai_daily.title": "头条 - {{title}}",
"ai_daily.tooltip.content": "这里是通过 AI 从您的时间线中选择的头条新闻(<From /> - <To />),可能对您很重要。",
"ai_daily.tooltip.update_schedule": "每天早上 8 点、晚上 8 点更新。",
"app.app_name": "APP_NAME",
"app.copy_logo_svg": "复制 Logo SVG",
"app.toggle_sidebar": "切换侧边栏",
"discover.any_url_or_keyword": "任何 URL 或关键词",
"discover.default_option": " (默认)",
"discover.feed_description": "此 Feed 的描述如下,您可以填写相关信息。",
Expand Down
1 change: 1 addition & 0 deletions locales/common/en.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"app.copied_to_clipboard": "Copied to clipboard",
"cancel": "Cancel",
"confirm": "Confirm",
"ok": "OK",
Expand Down
28 changes: 28 additions & 0 deletions locales/common/ja.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"app.copied_to_clipboard": "クリップボードにコピーしました",
"cancel": "キャンセル",
"confirm": "確認",
"ok": "OK",
"quantifier.piece": "",
"time.last_night": "昨夜",
"time.the_night_before_last": "一昨夜",
"time.today": "今日",
"time.yesterday": "昨日",
"tips.load-lng-error": "言語パックの読み込みに失敗しました",
"words.back": "戻る",
"words.copy": "コピー",
"words.edit": "編集",
"words.entry": "エントリー",
"words.id": "ID",
"words.items_one": "アイテム",
"words.items_other": "アイテム",
"words.local": "ローカル",
"words.record": "記録",
"words.record_one": "記録",
"words.record_other": "記録",
"words.result": "結果",
"words.result_one": "結果",
"words.result_other": "結果",
"words.space": "",
"words.which.all": "すべて"
}
1 change: 1 addition & 0 deletions locales/common/zh-CN.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"app.copied_to_clipboard": "已复制到剪贴板",
"cancel": "取消",
"confirm": "确认",
"ok": "",
Expand Down
3 changes: 2 additions & 1 deletion locales/lang/en.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"langs.en": "English",
"langs.zh-CN": "简体中文(部分完成)",
"langs.ja": "日本語",
"langs.zh-CN": "简体中文",
"name": "English"
}
6 changes: 6 additions & 0 deletions locales/lang/ja.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"langs.en": "English",
"langs.ja": "日本語",
"langs.zh-CN": "简体中文",
"name": "日本語"
}
5 changes: 3 additions & 2 deletions locales/lang/zh-CN.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"langs.en": "English",
"langs.zh-CN": "简体中文(部分完成)",
"name": "English"
"langs.ja": "日本語",
"langs.zh-CN": "简体中文",
"name": "简体中文"
}
11 changes: 2 additions & 9 deletions src/renderer/src/@types/constants.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
export const currentSupportedLanguages = (() => {
const langsFiles = import.meta.glob("../../../../locales/app/*.json")

const langs = [] as string[]
for (const key in langsFiles) {
langs.push(key.split("/").pop()?.replace(".json", "") as string)
}
return langs
})()
export const currentSupportedLanguages = ["en", "ja", "zh-CN"]

export const dayjsLocaleImportMap = {
en: ["en", () => import("dayjs/locale/en")],
["zh-CN"]: ["zh-cn", () => import("dayjs/locale/zh-cn")],
["ja"]: ["ja", () => import("dayjs/locale/ja")],
}

export const ns = ["app", "common", "lang", "settings", "shortcuts"] as const
Expand Down
6 changes: 6 additions & 0 deletions src/renderer/src/@types/default-resource.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import en from "../../../../locales/app/en.json"
import common_en from "../../../../locales/common/en.json"
import common_ja from "../../../../locales/common/ja.json"
import common_zhCN from "../../../../locales/common/zh-CN.json"
import external_en from "../../../../locales/external/en.json"
import lang_en from "../../../../locales/lang/en.json"
import lang_ja from "../../../../locales/lang/ja.json"
import lang_zhCN from "../../../../locales/lang/zh-CN.json"
import settings_en from "../../../../locales/settings/en.json"
import shortcuts_en from "../../../../locales/shortcuts/en.json"
Expand All @@ -28,4 +30,8 @@ export const defaultResources = {
lang: lang_zhCN,
common: common_zhCN,
},
ja: {
lang: lang_ja,
common: common_ja,
},
}
1 change: 1 addition & 0 deletions src/renderer/src/env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
declare const APP_VERSION: string
declare const APP_NAME: string
declare const RELEASE_CHANNEL: string
declare const I18N_COMPLETENESS_MAP: Record<string, number>
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { PopoverPortal } from "@radix-ui/react-popover"
import { ActionButton, Button, IconButton } from "@renderer/components/ui/button"
import { Kbd, KbdCombined } from "@renderer/components/ui/kbd/Kbd"
import { Popover, PopoverContent, PopoverTrigger } from "@renderer/components/ui/popover"
import { RootPortal } from "@renderer/components/ui/portal"
import { HotKeyScopeMap } from "@renderer/constants"
import { shortcuts } from "@renderer/constants/shortcuts"
Expand Down Expand Up @@ -176,65 +174,6 @@ const ConfirmMarkAllReadInfo = ({ undo }: { undo: () => any }) => {
)
}

/**
* @deprecated
*/
export const MarkAllReadPopover = forwardRef<HTMLButtonElement, MarkAllButtonProps>(
({ filter, className, which = "all", shortcut }, ref) => {
const [markPopoverOpen, setMarkPopoverOpen] = useState(false)

const handleMarkAllAsRead = useMarkAllByRoute(filter)

return (
<Popover open={markPopoverOpen} onOpenChange={setMarkPopoverOpen}>
<PopoverTrigger asChild>
<ActionButton
shortcut={shortcut ? shortcuts.entries.markAllAsRead.key : undefined}
tooltip={
<span>
Mark
<span> </span>
{which}
<span> </span>
as read
</span>
}
className={className}
ref={ref}
>
<i className="i-mgc-check-circle-cute-re" />
</ActionButton>
</PopoverTrigger>
<PopoverPortal>
<PopoverContent className="flex w-fit flex-col items-center justify-center gap-3 !py-3 [&_button]:text-xs">
<div className="text-sm">
<Trans
i18nKey="mark_all_read_button.mark_as_read"
values={{
// @ts-expect-error https://www.i18next.com/overview/typescript#type-error-template-literal
// should be fixed by using `as const` but it's not working
which: commonT(`words.which.${which}`),
}}
/>
</div>
<div className="space-x-4">
<IconButton
icon={<i className="i-mgc-check-filled" />}
onClick={() => {
handleMarkAllAsRead()
setMarkPopoverOpen(false)
}}
>
Confirm
</IconButton>
</div>
</PopoverContent>
</PopoverPortal>
</Popover>
)
},
)

export const FlatMarkAllReadButton: FC<MarkAllButtonProps> = (props) => {
const t = useI18n()

Expand Down
10 changes: 6 additions & 4 deletions src/renderer/src/modules/feed-column/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ActionButton } from "@renderer/components/ui/button"
import { Popover, PopoverContent, PopoverTrigger } from "@renderer/components/ui/popover"
import { ProfileButton } from "@renderer/components/user-button"
import { useNavigateEntry } from "@renderer/hooks/biz/useNavigateEntry"
import { useI18n } from "@renderer/hooks/common"
import { stopPropagation } from "@renderer/lib/dom"
import { cn } from "@renderer/lib/utils"
import { m } from "framer-motion"
Expand Down Expand Up @@ -53,7 +54,6 @@ export const FeedColumnHeader = memo(() => {
}}
>
<Logo className="mr-1 size-6" />

{APP_NAME}
</div>
</LogoContextMenu>
Expand All @@ -80,10 +80,11 @@ const LayoutActionButton = () => {
width: !feedColumnShow ? "auto" : 0,
})

const t = useI18n()
return (
<m.div initial={animation} animate={animation} className="overflow-hidden">
<ActionButton
tooltip="Toggle Sidebar"
tooltip={t("app.toggle_sidebar")}
icon={
<i
className={cn(
Expand All @@ -103,6 +104,7 @@ const LayoutActionButton = () => {
const LogoContextMenu: FC<PropsWithChildren> = ({ children }) => {
const [open, setOpen] = useState(false)
const logoRef = useRef<SVGSVGElement>(null)
const t = useI18n()

return (
<Popover open={open} onOpenChange={setOpen}>
Expand All @@ -120,7 +122,7 @@ const LogoContextMenu: FC<PropsWithChildren> = ({ children }) => {
onClick={() => {
navigator.clipboard.writeText(logoRef.current?.outerHTML || "")
setOpen(false)
toast.success("Copied to clipboard")
toast.success(t.common("app.copied_to_clipboard"))
}}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm px-1 py-0.5 text-sm outline-none",
Expand All @@ -129,7 +131,7 @@ const LogoContextMenu: FC<PropsWithChildren> = ({ children }) => {
)}
>
<Logo ref={logoRef} />
<span>Copy Logo SVG</span>
<span>{t("app.copy_logo_svg")}</span>
</button>
</PopoverContent>
</Popover>
Expand Down
15 changes: 10 additions & 5 deletions src/renderer/src/modules/settings/tabs/general.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -221,11 +221,16 @@ export const LanguageSelector = () => {
<SelectValue />
</SelectTrigger>
<SelectContent position="item-aligned">
{currentSupportedLanguages.map((lang) => (
<SelectItem key={lang} value={lang}>
{langT(`langs.${lang}` as any)}
</SelectItem>
))}
{currentSupportedLanguages.map((lang) => {
const percent = I18N_COMPLETENESS_MAP[lang]

return (
<SelectItem key={lang} value={lang}>
{langT(`langs.${lang}` as any)}{" "}
{typeof percent === "number" ? (percent === 100 ? null : `(${percent}%)`) : null}
</SelectItem>
)
})}
</SelectContent>
</Select>
</div>
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.node.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"src/env.ts",
"./src/hono.ts",
"src/shared/src/global.d.ts",
"configs/vite.render.config.ts",
"configs/*",
"./scripts/**/*"
],
"compilerOptions": {
Expand Down

0 comments on commit bf7da04

Please sign in to comment.