Server Components

This guide explains how to use the internationalization package with React Server Components in your Next.js application.

Overview

Server Components are rendered on the server and can directly access server-side resources. With the internationalization package, you can load translations directly in your Server Components without additional client-side JavaScript.

Loading Translations

To use translations in a Server Component, import the getDictionary function and use it to load the appropriate dictionary based on the locale:

import { getDictionary } from '@repo/internationalization';

export default async function ServerComponent({ params }: { params: { locale: string } }) {
  const dictionary = await getDictionary(params.locale);
  
  return (
    <div>
      <h1>{dictionary.web.header.home}</h1>
      <p>{dictionary.web.home.meta.description}</p>
    </div>
  );
}

The getDictionary function:

  1. Takes a locale string as input
  2. Loads the corresponding dictionary file
  3. Returns the dictionary object with all translations for that locale

Accessing Nested Translations

Dictionary files are structured as nested objects. You can access nested translations using dot notation:

// Access nested translations
<h2>{dictionary.web.home.features.title}</h2>
<p>{dictionary.web.home.features.description}</p>

Working with Arrays

If your translations include arrays, you can map over them to render multiple elements:

// Translation dictionary structure:
// {
//   "web": {
//     "home": {
//       "features": {
//         "items": [
//           { "title": "Feature 1", "description": "Description 1" },
//           { "title": "Feature 2", "description": "Description 2" }
//         ]
//       }
//     }
//   }
// }

{dictionary.web.home.features.items.map((feature, index) => (
  <div key={index} className="feature">
    <h3>{feature.title}</h3>
    <p>{feature.description}</p>
  </div>
))}

Handling Dynamic Content

You can combine translations with dynamic content:

// Assuming the translation has a placeholder: "Welcome, {name}!"
<p>{dictionary.web.greeting.replace('{name}', user.name)}</p>

// For more complex replacements, you can use a helper function
function formatMessage(template: string, values: Record<string, string>) {
  return template.replace(/{(\w+)}/g, (_, key) => values[key] || '');
}

<p>
  {formatMessage(dictionary.web.lastLogin, { 
    date: new Date().toLocaleDateString(params.locale),
    time: new Date().toLocaleTimeString(params.locale)
  })}
</p>

Fallback Handling

The getDictionary function includes built-in fallback handling. If a translation is not available in the requested locale, it will fall back to English:

// This will use the English translation if the Spanish one doesn't exist
const dictionary = await getDictionary('es');

You can also implement your own fallback logic for specific cases:

const title = dictionary.web.specialPage?.title || dictionary.web.defaultPage.title;

Metadata and SEO

You can use translations for page metadata to improve SEO:

import { getDictionary } from '@repo/internationalization';
import { Metadata } from 'next';

export async function generateMetadata({ 
  params 
}: { 
  params: { locale: string } 
}): Promise<Metadata> {
  const dictionary = await getDictionary(params.locale);
  
  return {
    title: dictionary.web.home.meta.title,
    description: dictionary.web.home.meta.description,
    openGraph: {
      title: dictionary.web.home.meta.title,
      description: dictionary.web.home.meta.description,
      locale: params.locale,
    },
  };
}

export default async function HomePage({ params }: { params: { locale: string } }) {
  const dictionary = await getDictionary(params.locale);
  
  return (
    <div>
      <h1>{dictionary.web.home.meta.title}</h1>
      <p>{dictionary.web.home.meta.description}</p>
    </div>
  );
}

Passing Translations to Client Components

To use translations in Client Components, you need to pass them from a Server Component:

import { getDictionary } from '@repo/internationalization';
import { TranslationProvider } from '@repo/internationalization/TranslationProvider';
import ClientComponent from '@/components/ClientComponent';

export default async function ServerComponent({ params }: { params: { locale: string } }) {
  const dictionary = await getDictionary(params.locale);
  
  return (
    <div>
      <h1>{dictionary.web.header.home}</h1>
      
      {/* Pass translations to Client Component */}
      <TranslationProvider locale={params.locale} messages={dictionary}>
        <ClientComponent />
      </TranslationProvider>
    </div>
  );
}

Best Practices

Examples

Basic Page with Translations

// app/[locale]/about/page.tsx
import { getDictionary } from '@repo/internationalization';

export default async function AboutPage({ params }: { params: { locale: string } }) {
  const dictionary = await getDictionary(params.locale);
  
  return (
    <div className="about-page">
      <h1>{dictionary.web.about.title}</h1>
      <p>{dictionary.web.about.description}</p>
      
      <section className="team">
        <h2>{dictionary.web.about.team.title}</h2>
        <div className="team-members">
          {dictionary.web.about.team.members.map((member, index) => (
            <div key={index} className="team-member">
              <h3>{member.name}</h3>
              <p>{member.role}</p>
              <p>{member.bio}</p>
            </div>
          ))}
        </div>
      </section>
    </div>
  );
}

Dynamic Routes with Translations

// app/[locale]/blog/[slug]/page.tsx
import { getDictionary } from '@repo/internationalization';

export default async function BlogPost({ 
  params 
}: { 
  params: { locale: string; slug: string } 
}) {
  const dictionary = await getDictionary(params.locale);
  const post = await fetchBlogPost(params.slug);
  
  return (
    <article>
      <h1>{post.title}</h1>
      <p className="date">
        {dictionary.web.blog.publishedOn.replace('{date}', 
          new Date(post.date).toLocaleDateString(params.locale)
        )}
      </p>
      <div className="content">{post.content}</div>
      
      <div className="related-posts">
        <h2>{dictionary.web.blog.relatedPosts}</h2>
        {/* Related posts content */}
      </div>
    </article>
  );
}

Next Steps