Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Global translation #93

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,17 @@ HYGRAPH_WEBOOK_SECRET // secret used for authenticating webhooks
```

Search engine - take it from your algolia project's config

```bash
NEXT_PUBLIC_ALGOLIA_API_ID
NEXT_PUBLIC_ALGOLIA_SEARCH_API_KEY
```

Analytics - used for counting views on your articles. Needed for 'Trending Articles' section

```bash
GA_MEASUREMENT_ID:
GA_PROPERTY_ID:
GA_MEASUREMENT_ID:
GA_PROPERTY_ID:
GA_BASE64_SERVICE_ACCOUNT:
```

Expand All @@ -73,7 +75,6 @@ yarn dev

5. Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.


## 🚀 Deployment

Easily deploy your Next.js app with [Vercel](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=github&utm_campaign=next-enterprise) by clicking the button below: //final repo name goes here
Expand Down
11 changes: 7 additions & 4 deletions src/app/[lang]/article/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { RichText } from "@/components/RichText/RichText"
import { ShareOnSocial } from "@/components/ShareOnSocial/ShareOnSocial"
import { env } from "@/env.mjs"
import { Locale } from "@/i18n/i18n"
import { getArticleBySlug, getArticleMetadataBySlug } from "@/lib/client"
import { getArticleBySlug, getArticleMetadataBySlug, getArticleTranslation } from "@/lib/client"
import { getMatadataObj } from "@/utils/getMetadataObj"

type ArticlePageProps = { params: { slug: string; lang: Locale } }
Expand All @@ -24,7 +24,10 @@ export async function generateMetadata({ params: { slug, lang } }: ArticlePagePr
}

export default async function Web({ params: { slug, lang } }: ArticlePageProps) {
const article = await getArticleBySlug({ locale: lang, slug })
const [article, translations] = await Promise.all([
getArticleBySlug({ locale: lang, slug }),
getArticleTranslation(lang),
])
const articleUrl = `${env.NEXT_PUBLIC_SITE_URL}/article/${slug}`
const initialQuiz = article?.content?.references[0]

Expand All @@ -46,15 +49,15 @@ export default async function Web({ params: { slug, lang } }: ArticlePageProps)
}}
asLink={false}
/>
<ShareOnSocial articleUrl={articleUrl} articleTitle={title} />
<ShareOnSocial shareOnSocialText={translations?.shareOnSocial} articleUrl={articleUrl} articleTitle={title} />
{article.content && (
<section className="flex w-full flex-col pt-8">
<RichText references={initialQuiz ? [initialQuiz] : []} raw={article.content.raw} />
</section>
)}
</article>
<NextIntlClientProvider locale={lang}>
<RecommendedArticles id={article.id} />
<RecommendedArticles titleText={translations?.relatedArticles} id={article.id} />
</NextIntlClientProvider>
</>
)
Expand Down
15 changes: 13 additions & 2 deletions src/app/[lang]/category/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { notFound } from "next/navigation"
import { Metadata } from "next/types"
import { CategoryArticles } from "@/components/CategoryArticles/CategoryArticles"
import { Locale } from "@/i18n/i18n"
import { getArticlesTranslationByCategory, listArticlesByCategory } from "@/lib/client"
import { getMatadataObj } from "@/utils/getMetadataObj"

type ArticlePageProps = { params: { slug: string; lang: Locale } }
Expand All @@ -9,6 +11,15 @@ export async function generateMetadata({ params: { slug } }: ArticlePageProps):
return getMatadataObj({ title: `Category - ${slug}` })
}

export default async function Web({ params: { slug } }: ArticlePageProps) {
return <CategoryArticles category={slug} />
export default async function Web({ params: { slug, lang } }: ArticlePageProps) {
const [articles, translations] = await Promise.all([
listArticlesByCategory({
locale: lang,
categorySlug: slug,
first: 4,
}),
getArticlesTranslationByCategory(lang),
])
if (!articles) return notFound()
return <CategoryArticles articles={articles} translations={translations} category={slug} />
}
7 changes: 4 additions & 3 deletions src/app/[lang]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Navigation } from "@/components/Navigation/Navigation"
import { env } from "@/env.mjs"
import { i18n, type Locale } from "@/i18n/i18n"
import "@/styles/tailwind.css"
import { getNavigation } from "@/lib/client"
import { getNavigation, getNavigationTranslation } from "@/lib/client"
import { GoogleAnalytics } from "../GoogleAnalytics"
import Providers from "../Providers"

Expand Down Expand Up @@ -41,7 +41,8 @@ export default async function Layout({ children, params }: { children: React.Rea
if (!isValidLocale) notFound()
unstable_setRequestLocale(locale)

const { navigation, footer, logo } = await getNavigation(locale)
const [navigationData, translations] = await Promise.all([getNavigation(locale), getNavigationTranslation(locale)])
const { navigation, footer, logo } = navigationData

return (
<html lang={locale}>
Expand All @@ -51,7 +52,7 @@ export default async function Layout({ children, params }: { children: React.Rea
<div className="z-50 flex w-full justify-center border-b bg-white">
<nav className="flex w-full max-w-[1200px] items-center justify-end gap-4 py-4">
<NextIntlClientProvider locale={locale}>
<Navigation navigation={navigation} />
<Navigation translations={translations} navigation={navigation} />
</NextIntlClientProvider>
</nav>
</div>
Expand Down
7 changes: 4 additions & 3 deletions src/app/[lang]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { RecentArticles } from "@/components/RecentArticles/RecentArticles"
import { StockDisplay } from "@/components/StockDisplay/StockDisplay"
import { TrendingArticles } from "@/components/TrendingArticles/TrendingArticles"
import { i18n, Locale } from "@/i18n/i18n"
import { getHomepage, getHomepageMetadata } from "@/lib/client"
import { getHomepage, getHomepageMetadata, getHomepageTranslation } from "@/lib/client"
import { getMatadataObj } from "@/utils/getMetadataObj"

export const dynamicParams = false
Expand All @@ -24,7 +24,8 @@ export async function generateMetadata({ params }: { params: { lang: Locale } })

export default async function Web({ params }: { params: { lang: Locale } }) {
unstable_setRequestLocale(params.lang)
const homepage = await getHomepage(params.lang)
const [homepage, translations] = await Promise.all([getHomepage(params.lang), getHomepageTranslation(params.lang)])
const showMoreText = translations?.showMore

return (
<>
Expand All @@ -50,7 +51,7 @@ export default async function Web({ params }: { params: { lang: Locale } }) {
categoryId={homepage.highlightedCategory.id}
/>
)}
<RecentArticles title={homepage.recentSectionTitle ?? "Recent articles"} />
<RecentArticles showMoreText={showMoreText} title={homepage.recentSectionTitle ?? "Recent articles"} />
</>
)
}
32 changes: 17 additions & 15 deletions src/components/CategoryArticles/CategoryArticles.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,36 @@
import { notFound } from "next/navigation"
import { NextIntlClientProvider } from "next-intl"
import { useLocale } from "@/i18n/i18n"
import { listArticlesByCategory } from "@/lib/client"
import { getArticlesTranslationByCategory, listArticlesByCategory } from "@/lib/client"
import { CategoryArticlesInfiniteDynamic } from "./CategoryArticlesInfiniteDynamic"

export const CATEGORY_ARTICLES_PER_PAGE = 4

type CategoryArticlesProps = {
category: string
translations: Awaited<ReturnType<typeof getArticlesTranslationByCategory>>
articles: Awaited<ReturnType<typeof listArticlesByCategory>>
}

export async function CategoryArticles({ category }: CategoryArticlesProps) {
export async function CategoryArticles({
category,
translations,
articles: { articles, count },
}: CategoryArticlesProps) {
const locale = useLocale()
const articles = await listArticlesByCategory({
locale: locale,
categorySlug: category,
first: CATEGORY_ARTICLES_PER_PAGE,
})

if (!articles) return notFound()
return (
<section className="w-full">
<div className="mb-10 w-full border-b-[1px] py-14">
<h2 className="mb-6 text-3xl font-bold">Search Category</h2>
<p className="mb-2 text-xs">Showing {articles.count} results for: </p>
<h2 className="mb-6 text-3xl font-bold">{translations?.searchCategory}</h2>
<p className="mb-2 text-xs">
{translations?.showing} {count} {translations?.resultsFor}:{" "}
</p>
<p className="text-xl font-bold">&quot;{category}&quot;</p>
</div>
<div className="mx-auto w-full">
<NextIntlClientProvider locale={locale}>
<CategoryArticlesInfiniteDynamic category={category} initialArticles={articles} />
<CategoryArticlesInfiniteDynamic
category={category}
showMoreText={translations?.showMore}
initialArticles={{ articles, count }}
/>
</NextIntlClientProvider>
</div>
</section>
Expand Down
9 changes: 5 additions & 4 deletions src/components/CategoryArticles/CategoryArticlesInfinite.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@ import { Button } from "@/components/ui/Button/Button"
import { ListArticlesByCategoryQuery } from "@/gql/graphql"
import { useLocale } from "@/i18n/i18n"
import { listArticlesByCategory } from "@/lib/client"
import { CATEGORY_ARTICLES_PER_PAGE } from "./CategoryArticles"
import { ArticlesGrid } from "../ArticlesGrid/ArticlesGrid"

export type CategoryArticlesInfiniteProps = {
initialArticles: { articles: ListArticlesByCategoryQuery["articles"]; count: number }
category: string
showMoreText: string | null | undefined
}

export function RecentArticlesInfinite({ initialArticles, category }: CategoryArticlesInfiniteProps) {
const CATEGORY_ARTICLES_PER_PAGE = 4

export function RecentArticlesInfinite({ initialArticles, category, showMoreText }: CategoryArticlesInfiniteProps) {
const locale = useLocale()

const {
Expand Down Expand Up @@ -41,7 +43,6 @@ export function RecentArticlesInfinite({ initialArticles, category }: CategoryAr
})

const articles = categoryArticlesQuery?.pages.flatMap((page) => page.articles)
const buttonText = isFetchingNextPage ? "Loading" : "See more"

return (
<>
Expand All @@ -52,7 +53,7 @@ export function RecentArticlesInfinite({ initialArticles, category }: CategoryAr
disabled={isFetchingNextPage}
onClick={() => fetchNextPage()}
>
{buttonText}
{showMoreText}
</Button>
)}
</>
Expand Down
11 changes: 7 additions & 4 deletions src/components/Navigation/Navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,22 @@ import Image from "next/image"
import Link from "next/link"
import { useState } from "react"
import { useLocale } from "@/i18n/i18n"
import { getNavigation } from "@/lib/client"
import { getNavigation, getNavigationTranslation } from "@/lib/client"
import { DynamicLangSelect } from "../LangSelect/DynamicLangSelect"
import { DynamicSearchDialog } from "../Search/DynamicSearchDialog"
import { Button } from "../ui/Button/Button"
import { Sheet, SheetContent, SheetTrigger } from "../ui/Sheet/Sheet"

export type Translations = Awaited<ReturnType<typeof getNavigationTranslation>>

export type GetNavigationReturn = Awaited<ReturnType<typeof getNavigation>>

type NavigationProps = {
navigation: Pick<GetNavigationReturn, "navigation">["navigation"]
translations: Translations
}

export function Navigation({ navigation }: NavigationProps) {
export function Navigation({ navigation, translations }: NavigationProps) {
const locale = useLocale()
const [isSheetOpen, setIsSheetOpen] = useState(false)
const { logo, elements } = navigation
Expand Down Expand Up @@ -47,7 +50,7 @@ export function Navigation({ navigation }: NavigationProps) {
</Link>
<ul className="hidden items-center gap-4 sm:flex-wrap lg:flex">
<li className="-mr-3 flex items-center">
<DynamicSearchDialog />
<DynamicSearchDialog translations={translations} />
</li>
{navElements}
<li>
Expand All @@ -56,7 +59,7 @@ export function Navigation({ navigation }: NavigationProps) {
</ul>
<ul className="flex items-center sm:flex-wrap lg:hidden">
<li className="flex items-center">
<DynamicSearchDialog />
<DynamicSearchDialog translations={translations} />
</li>
<Sheet open={isSheetOpen}>
<SheetTrigger asChild onClick={() => setIsSheetOpen((prev) => !prev)}>
Expand Down
5 changes: 3 additions & 2 deletions src/components/RecentArticles/RecentArticles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ export const RECENT_ARTICLES_PER_PAGE = 6

type RecentArticlesProps = {
title: string
showMoreText: string | null | undefined
}

export async function RecentArticles({ title }: RecentArticlesProps) {
export async function RecentArticles({ title, showMoreText }: RecentArticlesProps) {
const locale = useLocale()
const initialArticles = await getRecentArticlesWithMain({ locale, first: 3, skip: 1 })
const mainArticle = initialArticles.mainArticle[0]
Expand All @@ -27,7 +28,7 @@ export async function RecentArticles({ title }: RecentArticlesProps) {
/>
</div>
<NextIntlClientProvider locale={locale}>
<RecentArticlesInfiniteDynamic initialArticles={initialArticles} />
<RecentArticlesInfiniteDynamic showMoreText={showMoreText} initialArticles={initialArticles} />
</NextIntlClientProvider>
</section>
)
Expand Down
5 changes: 3 additions & 2 deletions src/components/RecentArticles/RecentArticlesInfinite.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ import { ArticleCard, hygraphArticleToCardProps } from "../ArticleCard/ArticleCa

export type RecentArticlesInfiniteProps = {
initialArticles: { articles: GetRecentArticlesQuery["articles"]; count: number }
showMoreText: string | undefined | null
}

export function RecentArticlesInfinite({ initialArticles }: RecentArticlesInfiniteProps) {
export function RecentArticlesInfinite({ initialArticles, showMoreText }: RecentArticlesInfiniteProps) {
const locale = useLocale()

const {
Expand Down Expand Up @@ -51,7 +52,7 @@ export function RecentArticlesInfinite({ initialArticles }: RecentArticlesInfini
</div>
{hasNextPage && (
<Button className="w-full rounded-xl border p-4" disabled={isFetchingNextPage} onClick={() => fetchNextPage()}>
See more
{showMoreText}
</Button>
)}
</section>
Expand Down
6 changes: 3 additions & 3 deletions src/components/RecommendedArticles/RecommendedArticles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { useLocale } from "@/i18n/i18n"
import { getArticleRecommendedArticles } from "@/lib/client"
import { ArticleCard, hygraphArticleToCardProps } from "../ArticleCard/ArticleCard"

type RecommendedArticlesProps = { id: string }
type RecommendedArticlesProps = { id: string; titleText: string | null | undefined }

export function RecommendedArticles({ id }: RecommendedArticlesProps) {
export function RecommendedArticles({ id, titleText }: RecommendedArticlesProps) {
const locale = useLocale()
const { data: recommendedArticles, isLoading } = useQuery({
queryKey: [`recommended-articles`, id],
Expand All @@ -17,7 +17,7 @@ export function RecommendedArticles({ id }: RecommendedArticlesProps) {
if (!isLoading && recommendedArticles?.length === 0) return null
return (
<section className="w-full py-4">
<h2 className="mb-8 text-2xl font-bold">Related articles</h2>
<h2 className="mb-8 text-2xl font-bold">{titleText}</h2>
<div className={`grid gap-8 md:grid-cols-3`}>
{isLoading &&
Array.from(Array(3).keys()).map((idx) => {
Expand Down
13 changes: 8 additions & 5 deletions src/components/Search/RefinementCombobox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@ import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem } from "
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/Popover/Popover"
import { cn } from "@/utils/cn"

type RefinementComboboxProps = UseRefinementListProps
type ExtendedRefinementComboboxProps = UseRefinementListProps & {
searchTagsText?: string | undefined | null
noTagsFoundText?: string | undefined | null
}

export function RefinementCombobox(props: RefinementComboboxProps) {
export function RefinementCombobox(props: ExtendedRefinementComboboxProps) {
const { items, refine } = useRefinementList(props)
const [open, setOpen] = React.useState(false)

Expand All @@ -26,7 +29,7 @@ export function RefinementCombobox(props: RefinementComboboxProps) {
aria-expanded={open}
className="w-[150px] justify-between bg-gray-100 text-gray-400"
>
{"Select tag..."}
{props?.searchTagsText}
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
Expand All @@ -48,8 +51,8 @@ export function RefinementCombobox(props: RefinementComboboxProps) {
</div>
<PopoverContent className="ml-6 w-[200px] bg-white p-0">
<Command>
<CommandInput placeholder="Search tags.." />
<CommandEmpty>No tags found.</CommandEmpty>
<CommandInput placeholder={props?.searchTagsText ?? "Search tags.."} />
<CommandEmpty>{props?.noTagsFoundText}</CommandEmpty>
<CommandGroup value={"value"}>
{items
.sort((a, b) => a.label.localeCompare(b.label))
Expand Down
Loading
Loading