diff --git a/api/index.js b/api/index.js index b09a87fe..f2fd54ec 100644 --- a/api/index.js +++ b/api/index.js @@ -28,6 +28,7 @@ module.exports = async (req, res) => { theme, cache_seconds, custom_title, + lang, } = req.query; let stats; @@ -36,6 +37,9 @@ module.exports = async (req, res) => { if (blacklist.includes(username)) { return res.send(renderError("Something went wrong")); } + if (lang && ![ "cn", "de", "en", "es", "fr", "it", "ja", "kr", "pt-br" ].includes(lang.toLowerCase())) { + return res.send(renderError("Something went wrong", "Language not found")); + } try { stats = await fetchStats( @@ -67,6 +71,7 @@ module.exports = async (req, res) => { bg_color, theme, custom_title, + lang: lang ? lang.toLowerCase() : null, }), ); } catch (err) { diff --git a/api/pin.js b/api/pin.js index 3486e70d..4d6c6fca 100644 --- a/api/pin.js +++ b/api/pin.js @@ -21,6 +21,7 @@ module.exports = async (req, res) => { theme, show_owner, cache_seconds, + lang, } = req.query; let repoData; @@ -31,6 +32,10 @@ module.exports = async (req, res) => { return res.send(renderError("Something went wrong")); } + if (lang && ![ "cn", "de", "en", "es", "fr", "it", "ja", "kr", "pt-br" ].includes(lang.toLowerCase())) { + return res.send(renderError("Something went wrong", "Language not found")); + } + try { repoData = await fetchRepo(username, repo); @@ -64,6 +69,7 @@ module.exports = async (req, res) => { bg_color, theme, show_owner: parseBoolean(show_owner), + lang: lang ? lang.toLowerCase() : null, }), ); } catch (err) { diff --git a/api/top-langs.js b/api/top-langs.js index 77bcc19b..b8e0d705 100644 --- a/api/top-langs.js +++ b/api/top-langs.js @@ -26,6 +26,7 @@ module.exports = async (req, res) => { langs_count, exclude_repo, custom_title, + lang, } = req.query; let topLangs; @@ -35,6 +36,10 @@ module.exports = async (req, res) => { return res.send(renderError("Something went wrong")); } + if (lang && ![ "cn", "de", "en", "es", "fr", "it", "ja", "kr", "pt-br" ].includes(lang.toLowerCase())) { + return res.send(renderError("Something went wrong", "Language not found")); + } + try { topLangs = await fetchTopLanguages( username, @@ -62,6 +67,7 @@ module.exports = async (req, res) => { bg_color, theme, layout, + lang: lang ? lang.toLowerCase() : null, }), ); } catch (err) { diff --git a/api/wakatime.js b/api/wakatime.js index 7277c9d9..75878b37 100644 --- a/api/wakatime.js +++ b/api/wakatime.js @@ -22,10 +22,15 @@ module.exports = async (req, res) => { hide_title, hide_progress, custom_title, + lang, } = req.query; res.setHeader("Content-Type", "image/svg+xml"); + if (lang && ![ "cn", "de", "en", "es", "fr", "it", "ja", "kr", "pt-br" ].includes(lang.toLowerCase())) { + return res.send(renderError("Something went wrong", "Language not found")); + } + try { const last7Days = await fetchLast7Days({ username }); @@ -53,6 +58,7 @@ module.exports = async (req, res) => { bg_color, theme, hide_progress, + lang: lang ? lang.toLowerCase() : null, }), ); } catch (err) { diff --git a/src/cards/repo-card.js b/src/cards/repo-card.js index 7b021ca0..2f11c8a7 100644 --- a/src/cards/repo-card.js +++ b/src/cards/repo-card.js @@ -28,8 +28,34 @@ const renderRepoCard = (repo, options = {}) => { bg_color, show_owner, theme = "default_repocard", + lang = "en", } = options; + const translations = { + template: { + cn: "模板", + de: "Vorlage", + en: "Template", + es: "Modelo", + fr: "Modèle", + it: "Modella", + ja: "テンプレート", + kr: "주형", + "pt-br": "Modelo", + }, + archived: { + cn: "已封存", + de: "Archiviert", + en: "Archived", + es: "Archivé", + fr: "Archivé", + it: "Archiviata", + ja: "アーカイブ済み", + kr: "보관 됨", + "pt-br": "Arquivada", + }, + } + const header = show_owner ? nameWithOwner : name; const langName = (primaryLanguage && primaryLanguage.name) || "Unspecified"; const langColor = (primaryLanguage && primaryLanguage.color) || "#333"; @@ -132,9 +158,9 @@ const renderRepoCard = (repo, options = {}) => { return card.render(` ${ isTemplate - ? getBadgeSVG("Template") + ? getBadgeSVG(translations.template[lang] || "Template") : isArchived - ? getBadgeSVG("Archived") + ? getBadgeSVG(translations.archived[lang] || "Archived") : "" } diff --git a/src/cards/stats-card.js b/src/cards/stats-card.js index 4954c719..87f77c34 100644 --- a/src/cards/stats-card.js +++ b/src/cards/stats-card.js @@ -66,6 +66,7 @@ const renderStatsCard = (stats = {}, options = { hide: [] }) => { bg_color, theme = "default", custom_title, + lang = "en", } = options; const lheight = parseInt(line_height, 10); @@ -79,17 +80,87 @@ const renderStatsCard = (stats = {}, options = { hide: [] }) => { theme, }); + const apostrophe = ["x", "s"].includes(name.slice(-1)) ? "" : "s"; + const translations = { + title: { + cn: `${encodeHTML(name)}的GitHub统计`, + de: `${encodeHTML(name) + apostrophe} GitHub-Statistiken`, + en: `${encodeHTML(name)}'${apostrophe} GitHub Stats`, + es: `Estadísticas de GitHub de ${encodeHTML(name)}`, + fr: `Statistiques GitHub de ${encodeHTML(name)}`, + it: `Statistiche GitHub di ${encodeHTML(name)}`, + ja: `${encodeHTML(name)}のGitHub統計`, + kr: `${encodeHTML(name)}의 GitHub 통계`, + "pt-br": `Estatísticas do GitHub de ${encodeHTML(name)}`, + }, + stars: { + cn: "总星数", + de: "Insgesamt Sterne", + en: "Total Stars", + es: "Estrellas totales", + fr: "Total d'étoiles", + it: "Stelle totali", + ja: "星の合計", + kr: "총 별", + "pt-br": "Total de estrelas", + }, + commits: { + cn: "总承诺", + de: "Total Commits", + en: "Total Commits", + es: "Compromisos totales", + fr: "Total des engagements", + it: "Impegni totali", + ja: "総コミット", + kr: "총 커밋", + "pt-br": "Total de compromissos", + }, + prs: { + cn: "总公关", + de: "PRs insgesamt", + en: "Total PRs", + es: "RP totales", + fr: "Total des PR", + it: "PR totali", + ja: "合計PR", + kr: "총 PR", + "pt-br": "Total de PRs", + }, + issues: { + cn: "总发行量", + de: "Gesamtausgaben", + en: "Total Issues", + es: "Problemas totales", + fr: "Nombre total de problèmes", + it: "Problemi totali", + ja: "総問題", + kr: "총 문제", + "pt-br": "Total de problemas", + }, + contribs: { + cn: "有助于", + de: "Beigetragen zu", + en: "Contributed to", + es: "Contribuido a", + fr: "Contribué à", + it: "Ha contribuito a", + ja: "に貢献しました", + kr: "에 기여하다", + "pt-br": "Contribuiu para", + } + } + // Meta data for creating text nodes with createTextNode function const STATS = { stars: { icon: icons.star, - label: "Total Stars", + label: translations.stars[lang] || "Total Stars", value: totalStars, id: "stars", }, commits: { icon: icons.commits, - label: `Total Commits${ + label: `${translations.commits[lang] || "Total Commits"}${ include_all_commits ? "" : ` (${new Date().getFullYear()})` }`, value: totalCommits, @@ -97,19 +168,19 @@ const renderStatsCard = (stats = {}, options = { hide: [] }) => { }, prs: { icon: icons.prs, - label: "Total PRs", + label: translations.prs[lang] || "Total PRs", value: totalPRs, id: "prs", }, issues: { icon: icons.issues, - label: "Total Issues", + label: translations.issues[lang] || "Total Issues", value: totalIssues, id: "issues", }, contribs: { icon: icons.contribs, - label: "Contributed to", + label: translations.contribs[lang] || "Contributed to", value: contributedTo, id: "contribs", }, @@ -166,10 +237,9 @@ const renderStatsCard = (stats = {}, options = { hide: [] }) => { progress, }); - const apostrophe = ["x", "s"].includes(name.slice(-1)) ? "" : "s"; const card = new Card({ customTitle: custom_title, - defaultTitle: `${encodeHTML(name)}'${apostrophe} GitHub Stats`, + defaultTitle: translations.title[lang] || `${encodeHTML(name)}'${apostrophe} GitHub Stats`, width: 495, height, colors: { diff --git a/src/cards/top-languages-card.js b/src/cards/top-languages-card.js index 8e85b72a..1c17bbec 100644 --- a/src/cards/top-languages-card.js +++ b/src/cards/top-languages-card.js @@ -70,11 +70,26 @@ const renderTopLanguages = (topLangs, options = {}) => { theme, layout, custom_title, + lang= "en", } = options; let langs = Object.values(topLangs); let langsToHide = {}; + const translations = { + title: { + cn: "最常用的语言", + de: "Meist verwendete Sprachen", + en: "Most Used Languages", + es: "Idiomas más usados", + fr: "Langues les plus utilisées", + it: "Lingue più utilizzate", + ja: "最もよく使われる言語", + kr: "가장 많이 사용되는 언어", + "pt-br": "Línguas Mais Usadas", + }, + } + // populate langsToHide map for quick lookup // while filtering out if (hide) { @@ -172,7 +187,7 @@ const renderTopLanguages = (topLangs, options = {}) => { const card = new Card({ customTitle: custom_title, - defaultTitle: "Most Used Languages", + defaultTitle: translations.title[lang] || "Most Used Languages", width, height, colors: { diff --git a/src/cards/wakatime-card.js b/src/cards/wakatime-card.js index bf103f50..0c0e5559 100644 --- a/src/cards/wakatime-card.js +++ b/src/cards/wakatime-card.js @@ -3,9 +3,9 @@ const { getStyles } = require("../getStyles"); const { getCardColors, FlexLayout } = require("../common/utils"); const { createProgressNode } = require("../common/createProgressNode"); -const noCodingActivityNode = ({ color }) => { +const noCodingActivityNode = ({ color, text }) => { return ` - No coding activity this week + ${text} `; }; @@ -60,8 +60,34 @@ const renderWakatimeCard = (stats = {}, options = { hide: [] }) => { theme = "default", hide_progress, custom_title, + lang = "en", } = options; + const translations = { + title: { + cn: "Wakatime周统计", + de: "Wakatime Week Stats", + en: "Wakatime Week Stats", + es: "Estadísticas de la semana de Wakatime", + fr: "Statistiques de la semaine Wakatime", + it: "Statistiche della settimana di Wakatime", + ja: "ワカタイムウィーク統計", + kr: "Wakatime 주간 통계", + "pt-br": "Estatísticas da semana Wakatime", + }, + noCodingActivity: { + cn: "本周没有编码活动", + de: "Keine Codierungsaktivität diese Woche", + en: "No coding activity this week", + es: "No hay actividad de codificación esta semana", + fr: "Aucune activité de codage cette semaine", + it: "Nessuna attività di codifica questa settimana", + ja: "今週のコーディング活動はありません", + kr: "이번 주 코딩 활동 없음", + "pt-br": "Nenhuma atividade de codificação esta semana", + }, + } + const lheight = parseInt(line_height, 10); // returns theme based colors with proper overrides and defaults @@ -101,7 +127,7 @@ const renderWakatimeCard = (stats = {}, options = { hide: [] }) => { const card = new Card({ customTitle: custom_title, - defaultTitle: "Wakatime Week Stats", + defaultTitle: translations.title[lang] || "Wakatime Week Stats", width: 495, height, colors: { @@ -126,7 +152,7 @@ const renderWakatimeCard = (stats = {}, options = { hide: [] }) => { ${FlexLayout({ items: statItems.length ? statItems - : [noCodingActivityNode({ color: textColor })], + : [noCodingActivityNode({ color: textColor, text: translations.noCodingActivity[lang] || "No coding activity this week" })], gap: lheight, direction: "column", }).join("")}