From 24f7ecb7957f43d83af1576a7ee44eb24bae4865 Mon Sep 17 00:00:00 2001 From: Chubby Granny Chaser Date: Fri, 17 Oct 2025 15:04:42 +0100 Subject: [PATCH] feat: adding translations --- src/locales/en/translation.json | 6 +- src/locales/pt-BR/translation.json | 4 + src/locales/ru/translation.json | 4 + .../src/pages/game-details/game-reviews.tsx | 13 +-- .../src/pages/game-details/review-item.scss | 23 +++++ .../src/pages/game-details/review-item.tsx | 84 +++++++++++++++++-- src/types/index.ts | 4 + 7 files changed, 124 insertions(+), 14 deletions(-) create mode 100644 src/renderer/src/pages/game-details/review-item.scss diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index bf2793c5..46bdb28c 100755 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -357,7 +357,11 @@ "delete_review_modal_description": "This action cannot be undone.", "delete_review_modal_delete_button": "Delete", "delete_review_modal_cancel_button": "Cancel", - "vote_failed": "Failed to register your vote. Please try again." + "vote_failed": "Failed to register your vote. Please try again.", + "show_original": "Show original", + "show_translation": "Show translation", + "show_original_translated_from": "Show original (translated from {{language}})", + "hide_original": "Hide original" }, "activation": { "title": "Activate Hydra", diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index c969e3bf..4ea77015 100755 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -345,6 +345,10 @@ "delete_review_modal_description": "Esta ação não pode ser desfeita.", "delete_review_modal_delete_button": "Excluir", "delete_review_modal_cancel_button": "Cancelar", + "show_original": "Mostrar original", + "show_translation": "Mostrar tradução", + "show_original_translated_from": "Mostrar original (traduzido do {{language}})", + "hide_original": "Ocultar original", "rating_count": "Avaliação" }, "activation": { diff --git a/src/locales/ru/translation.json b/src/locales/ru/translation.json index 895db29d..886c7d07 100644 --- a/src/locales/ru/translation.json +++ b/src/locales/ru/translation.json @@ -259,6 +259,10 @@ "delete_review_modal_description": "Это действие нельзя отменить.", "delete_review_modal_delete_button": "Удалить", "delete_review_modal_cancel_button": "Отмена", + "show_original": "Показать оригинал", + "show_translation": "Показать перевод", + "show_original_translated_from": "Показать оригинал (переведено с {{language}})", + "hide_original": "Скрыть оригинал", "cloud_save": "Облачное сохранение", "cloud_save_description": "Сохраняйте ваш прогресс в облаке и продолжайте играть на любом устройстве", "backups": "Резервные копии", diff --git a/src/renderer/src/pages/game-details/game-reviews.tsx b/src/renderer/src/pages/game-details/game-reviews.tsx index 851852b2..f8117f43 100644 --- a/src/renderer/src/pages/game-details/game-reviews.tsx +++ b/src/renderer/src/pages/game-details/game-reviews.tsx @@ -39,7 +39,7 @@ export function GameReviews({ hasUserReviewed, onUserReviewedChange, }: Readonly) { - const { t } = useTranslation("game_details"); + const { t, i18n } = useTranslation("game_details"); const { showSuccessToast, showErrorToast } = useToast(); const [reviews, setReviews] = useState([]); @@ -129,9 +129,7 @@ export function GameReviews({ const twoHoursInMilliseconds = 2 * 60 * 60 * 1000; const hasEnoughPlaytime = - game && - game.playTimeInMilliseconds >= twoHoursInMilliseconds && - !game.hasManuallyUpdatedPlaytime; + game && game.playTimeInMilliseconds >= twoHoursInMilliseconds; if ( !hasReviewed && @@ -146,6 +144,8 @@ export function GameReviews({ } }, [objectId, userDetailsId, shop, game, onUserReviewedChange]); + console.log("reviews", reviews); + const loadReviews = useCallback( async (reset = false) => { if (!objectId) return; @@ -164,6 +164,7 @@ export function GameReviews({ take: "20", skip: skip.toString(), sortBy: reviewsSortBy, + language: i18n.language, }); const response = await window.electron.hydraApi.get( @@ -200,7 +201,7 @@ export function GameReviews({ } } }, - [objectId, shop, reviewsPage, reviewsSortBy] + [objectId, shop, reviewsPage, reviewsSortBy, i18n.language] ); const handleVoteReview = async ( @@ -439,6 +440,8 @@ export function GameReviews({ }); }, [reviews]); + console.log("reviews", reviews); + return (
{showReviewPrompt && diff --git a/src/renderer/src/pages/game-details/review-item.scss b/src/renderer/src/pages/game-details/review-item.scss new file mode 100644 index 00000000..e1651ef5 --- /dev/null +++ b/src/renderer/src/pages/game-details/review-item.scss @@ -0,0 +1,23 @@ +@use "../../scss/globals.scss"; + +.game-details { + &__review-translation-toggle { + display: inline-flex; + align-items: center; + gap: calc(globals.$spacing-unit * 1); + margin-top: calc(globals.$spacing-unit * 1.5); + padding: 0; + background: none; + border: none; + color: rgba(255, 255, 255, 0.6); + font-size: 0.875rem; + cursor: pointer; + text-decoration: none; + transition: all 0.2s ease; + + &:hover { + text-decoration: underline; + color: rgba(255, 255, 255, 0.9); + } + } +} diff --git a/src/renderer/src/pages/game-details/review-item.tsx b/src/renderer/src/pages/game-details/review-item.tsx index 85c81e70..f5e3528a 100644 --- a/src/renderer/src/pages/game-details/review-item.tsx +++ b/src/renderer/src/pages/game-details/review-item.tsx @@ -1,8 +1,9 @@ import { TrashIcon, ClockIcon } from "@primer/octicons-react"; -import { ThumbsUp, ThumbsDown, Star } from "lucide-react"; +import { ThumbsUp, ThumbsDown, Star, Languages } from "lucide-react"; import { useNavigate } from "react-router-dom"; import { motion, AnimatePresence } from "framer-motion"; import { useTranslation } from "react-i18next"; +import { useState } from "react"; import type { GameReview } from "@types"; import { sanitizeHtml } from "@shared"; @@ -10,6 +11,8 @@ import { useDate } from "@renderer/hooks"; import { formatNumber } from "@renderer/helpers"; import { Avatar } from "@renderer/components"; +import "./review-item.scss"; + interface ReviewItemProps { review: GameReview; userDetailsId?: string; @@ -63,9 +66,45 @@ export function ReviewItem({ onAnimationComplete, }: Readonly) { const navigate = useNavigate(); - const { t } = useTranslation("game_details"); + const { t, i18n } = useTranslation("game_details"); const { formatDistance } = useDate(); + const [showOriginal, setShowOriginal] = useState(false); + + // Check if this is the user's own review + const isOwnReview = userDetailsId === review.user.id; + + // Helper to get base language code (e.g., "pt" from "pt-BR") + const getBaseLanguage = (lang: string) => lang.split("-")[0]; + + // Check if the review is in a different language (comparing base language codes) + const isDifferentLanguage = + getBaseLanguage(review.detectedLanguage) !== getBaseLanguage(i18n.language); + + // Check if translation is available and needed (but not for own reviews) + const needsTranslation = + !isOwnReview && + isDifferentLanguage && + review.translations && + review.translations[i18n.language]; + + // Get the full language name using Intl.DisplayNames + const getLanguageName = (languageCode: string) => { + try { + const displayNames = new Intl.DisplayNames([i18n.language], { + type: "language", + }); + return displayNames.of(languageCode) || languageCode.toUpperCase(); + } catch { + return languageCode.toUpperCase(); + } + }; + + // Determine which content to show - always show original for own reviews + const displayContent = needsTranslation + ? review.translations[i18n.language] + : review.reviewHtml; + if (isBlocked && !isVisible) { return (
@@ -135,12 +174,41 @@ export function ReviewItem({ ))}
-
+
+
+ {needsTranslation && ( + <> + + {showOriginal && ( +
+ )} + + )} +