language i18n added and tested for server and client side
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
export default function AuthLayout({ children }: { children: ReactNode }) {
|
||||
return <>{children}</>;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export default function SelectPage() {
|
||||
return <div></div>;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export default function DashboardPage() {
|
||||
return <div></div>;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
export default function ProtectedLayout({ children }: { children: ReactNode }) {
|
||||
return <>{children}</>;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
'use client';
|
||||
import { useTranslations } from 'next-intl';
|
||||
|
||||
export default function IndexPage() {
|
||||
const t = useTranslations('Index');
|
||||
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto p-4">
|
||||
<h1 className="text-2xl font-bold mb-4">{t('title')}</h1>
|
||||
<p className="mb-4">{t('description')}</p>
|
||||
|
||||
<div className="my-8">
|
||||
<h2 className="text-xl font-semibold mb-2">{t('navigation.title')}</h2>
|
||||
<ul className="list-disc pl-5">
|
||||
<li>{t('navigation.home')}</li>
|
||||
<li>{t('navigation.about')}</li>
|
||||
<li>{t('navigation.contact')}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="my-8">
|
||||
<h2 className="text-xl font-semibold mb-2">{t('auth.title')}</h2>
|
||||
<div className="flex gap-2">
|
||||
<button className="px-4 py-2 bg-blue-500 text-white rounded">
|
||||
{t('auth.login')}
|
||||
</button>
|
||||
<button className="px-4 py-2 bg-green-500 text-white rounded">
|
||||
{t('auth.register')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// Server Component
|
||||
import { getTranslations } from 'next-intl/server';
|
||||
import { Link } from '@/i18n/navigation';
|
||||
import { Locale } from '@/i18n/locales';
|
||||
import LocaleSwitcherServer from '@/components/LocaleSwitcherServer';
|
||||
|
||||
// Define the props type to get the locale parameter
|
||||
type Props = {
|
||||
params: Promise<{ locale: string }>;
|
||||
};
|
||||
|
||||
export default async function HomePage({ params }: Props) {
|
||||
// Get the locale from params
|
||||
const { locale } = await params;
|
||||
|
||||
// Get translations with the correct locale
|
||||
const t = await getTranslations({ locale: locale as Locale, namespace: 'Index' });
|
||||
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto p-4">
|
||||
<h1 className="text-2xl font-bold mb-4">{t('title')}</h1>
|
||||
<p className="mb-4">{t('description')}</p>
|
||||
|
||||
<div className="my-8">
|
||||
<h2 className="text-xl font-semibold mb-2">{t('navigation.title')}</h2>
|
||||
<ul className="list-disc pl-5 mb-4">
|
||||
<li>
|
||||
<Link href="/">{t('navigation.home')}</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/about">{t('navigation.about')}</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/contact">{t('navigation.contact')}</Link>
|
||||
</li>
|
||||
</ul>
|
||||
<p>{t('navigation.current')} </p>
|
||||
<LocaleSwitcherServer locale={locale} pathname="/home" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
export default function PublicLayout({ children }: { children: ReactNode }) {
|
||||
return <>{children}</>;
|
||||
}
|
||||
59
ServicesFrontEnd/frontend/src/app/[locale]/layout.tsx
Normal file
59
ServicesFrontEnd/frontend/src/app/[locale]/layout.tsx
Normal file
@@ -0,0 +1,59 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { Inter } from 'next/font/google';
|
||||
import { notFound } from "next/navigation";
|
||||
import { getTranslations } from "next-intl/server";
|
||||
import { Locale, locales } from "@/i18n/locales";
|
||||
import { routing } from "@/i18n/routing";
|
||||
import { NextIntlClientProvider } from 'next-intl';
|
||||
import '../globals.css';
|
||||
|
||||
const inter = Inter({ subsets: ["latin"] });
|
||||
|
||||
type Props = {
|
||||
children: ReactNode;
|
||||
params: Promise<{ locale: Locale }>;
|
||||
};
|
||||
|
||||
export function generateStaticParams() {
|
||||
return locales.map((locale) => ({ locale }));
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params }: Omit<Props, 'children'>) {
|
||||
// Properly await params before accessing properties
|
||||
const { locale } = await params;
|
||||
const t = await getTranslations({ locale, namespace: 'LocaleLayout' });
|
||||
|
||||
return {
|
||||
title: t('title')
|
||||
};
|
||||
}
|
||||
|
||||
export default async function LocaleLayout({ children, params }: Props) {
|
||||
// Properly await params before accessing properties
|
||||
const { locale } = await params;
|
||||
|
||||
// Validate that the incoming locale is valid
|
||||
if (!locales.includes(locale as Locale)) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
// Load messages for all child components
|
||||
const messages = (await import(`@/i18n/${locale}.json`)).default;
|
||||
|
||||
// Enable static rendering
|
||||
// Note: unstable_setRequestLocale is removed as it's causing TypeScript errors
|
||||
|
||||
return (
|
||||
<html lang={locale}>
|
||||
<body className={inter.className}>
|
||||
<NextIntlClientProvider
|
||||
locale={locale}
|
||||
messages={messages}
|
||||
timeZone="Europe/Istanbul" // Configure a default timezone for Turkey
|
||||
>
|
||||
{children}
|
||||
</NextIntlClientProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
6
ServicesFrontEnd/frontend/src/app/[locale]/page.tsx
Normal file
6
ServicesFrontEnd/frontend/src/app/[locale]/page.tsx
Normal file
@@ -0,0 +1,6 @@
|
||||
'use server';
|
||||
import HomePage from '@/app/home-page';
|
||||
|
||||
export default async function Home() {
|
||||
return <HomePage />;
|
||||
}
|
||||
BIN
ServicesFrontEnd/frontend/src/app/favicon.ico
Normal file
BIN
ServicesFrontEnd/frontend/src/app/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
26
ServicesFrontEnd/frontend/src/app/globals.css
Normal file
26
ServicesFrontEnd/frontend/src/app/globals.css
Normal file
@@ -0,0 +1,26 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
:root {
|
||||
--background: #ffffff;
|
||||
--foreground: #171717;
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--font-sans: var(--font-geist-sans);
|
||||
--font-mono: var(--font-geist-mono);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--background: #0a0a0a;
|
||||
--foreground: #ededed;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--background);
|
||||
color: var(--foreground);
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
29
ServicesFrontEnd/frontend/src/app/home-page.tsx
Normal file
29
ServicesFrontEnd/frontend/src/app/home-page.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
'use client';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useRouter } from '@/i18n/navigation';
|
||||
import { useParams } from 'next/navigation';
|
||||
import LocaleSwitcherClient from '@/components/LocaleSwitcherClient';
|
||||
|
||||
export default function HomePage() {
|
||||
const t = useTranslations('Index');
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
|
||||
const handleNavigation = (path: string) => {
|
||||
console.log('Navigating to /about');
|
||||
router.push(path);
|
||||
};
|
||||
|
||||
return (
|
||||
<main>
|
||||
<h1>{t('title')}</h1>
|
||||
<p>{t('description')}</p>
|
||||
<p>{t('navigation.title')} : {params.locale}</p>
|
||||
<div className='flex flex-col gap-2'>
|
||||
<LocaleSwitcherClient />
|
||||
<button onClick={() => handleNavigation('/about')}>{t('navigation.about')}</button>
|
||||
<button onClick={() => handleNavigation('/home')}>{t('navigation.home')}</button>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
'use client';
|
||||
|
||||
import { usePathname } from 'next-intl/client';
|
||||
import { Link, useRouter } from '@/i18n/routing';
|
||||
import { locales } from '@/i18n/locales';
|
||||
|
||||
export default function LanguageSwitcher() {
|
||||
const pathname = usePathname();
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>Select language:</p>
|
||||
<ul>
|
||||
{locales.map((locale) => (
|
||||
<li key={locale}>
|
||||
<Link href={pathname} locale={locale}>
|
||||
{locale.toUpperCase()}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
'use client';
|
||||
|
||||
import { useRouter, usePathname } from '@/i18n/navigation';
|
||||
import { locales, Locale } from '@/i18n/locales';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useParams } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Client Component for switching locales
|
||||
* Uses the useRouter hook from next-intl/navigation
|
||||
* Renders as a dropdown that excludes the current language
|
||||
*/
|
||||
export default function LocaleSwitcherClient({ className = '' }: Props) {
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const params = useParams();
|
||||
const currentLocale = params.locale as Locale;
|
||||
const t = useTranslations('Index');
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
// Filter out the current locale from the available options
|
||||
const availableLocales = locales.filter(lang => lang !== currentLocale);
|
||||
|
||||
const handleLocaleChange = (newLocale: Locale) => {
|
||||
// Navigate to the same path but with a different locale
|
||||
router.push(pathname, { locale: newLocale });
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`relative inline-block ${className}`}>
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex justify-between items-center w-24 rounded-md border border-gray-300 px-4 py-2 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
aria-expanded={isOpen}
|
||||
aria-haspopup="true"
|
||||
>
|
||||
<span>{currentLocale.toUpperCase()}</span>
|
||||
<svg className="-mr-1 ml-2 h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
||||
<path fillRule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clipRule="evenodd" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{isOpen && (
|
||||
<div
|
||||
className="origin-top-right mt-2 w-24 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none z-10"
|
||||
role="menu"
|
||||
aria-orientation="vertical"
|
||||
tabIndex={-1}
|
||||
>
|
||||
<div className="py-1" role="none">
|
||||
{availableLocales.map((lang) => (
|
||||
<button
|
||||
key={lang}
|
||||
onClick={() => handleLocaleChange(lang)}
|
||||
className="block w-full text-center px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900"
|
||||
role="menuitem"
|
||||
>
|
||||
{lang.toUpperCase()}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import { Link } from '@/i18n/navigation';
|
||||
import { locales, Locale } from '@/i18n/locales';
|
||||
import { getTranslations } from 'next-intl/server';
|
||||
import { headers } from 'next/headers';
|
||||
|
||||
type Props = {
|
||||
locale: string;
|
||||
className?: string;
|
||||
pathname?: string; // Optional pathname to navigate to
|
||||
};
|
||||
|
||||
/**
|
||||
* Server Component for switching locales
|
||||
* Uses the Link component from next-intl/navigation
|
||||
* Renders as a dropdown that excludes the current language
|
||||
*/
|
||||
export default async function LocaleSwitcherServer({ locale, className = '', pathname }: Props) {
|
||||
const t = await getTranslations({ locale: locale as Locale, namespace: 'Index' });
|
||||
|
||||
// Use the provided pathname or default to the current route
|
||||
let pathWithoutLocale = pathname || '/home';
|
||||
|
||||
// If we're on a locale-prefixed route like /en/home or /tr/about,
|
||||
// extract just the path part without the locale prefix
|
||||
if (pathWithoutLocale.startsWith(`/${locale}/`)) {
|
||||
pathWithoutLocale = pathWithoutLocale.substring(locale.length + 1);
|
||||
} else if (pathWithoutLocale.startsWith('/')) {
|
||||
// If it already starts with /, keep it as is
|
||||
} else {
|
||||
// Ensure it starts with /
|
||||
pathWithoutLocale = `/${pathWithoutLocale}`;
|
||||
}
|
||||
|
||||
// Filter out the current locale from the available options
|
||||
const availableLocales = locales.filter(lang => lang !== locale);
|
||||
|
||||
return (
|
||||
<div className={`relative inline-block ${className}`}>
|
||||
<div className="group">
|
||||
<button type="button" className="inline-flex justify-between items-center w-24 rounded-md border border-gray-300 px-4 py-2 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
|
||||
<span>{locale.toUpperCase()}</span>
|
||||
<svg className="-mr-1 ml-2 h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
||||
<path fillRule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clipRule="evenodd" />
|
||||
</svg>
|
||||
</button>
|
||||
<div className="hidden group-hover:block mt-2 w-24 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none z-10">
|
||||
<div className="py-1" role="menu" aria-orientation="vertical">
|
||||
{availableLocales.map((lang) => (
|
||||
<Link
|
||||
key={lang}
|
||||
href={pathWithoutLocale}
|
||||
locale={lang}
|
||||
className="block px-4 py-2 text-sm text-center text-gray-700 hover:bg-gray-100 hover:text-gray-900"
|
||||
role="menuitem"
|
||||
>
|
||||
{lang.toUpperCase()}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
'use client';
|
||||
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { NextIntlClientProvider } from 'next-intl';
|
||||
import { Locale } from '@/i18n/locales';
|
||||
|
||||
type NamespaceType = 'Index' | 'LocaleLayout';
|
||||
|
||||
interface TranslatedContentProps {
|
||||
namespace: NamespaceType;
|
||||
locale: Locale;
|
||||
messages: any;
|
||||
}
|
||||
|
||||
export default function TranslatedContent({ namespace, locale, messages }: TranslatedContentProps) {
|
||||
return (
|
||||
<NextIntlClientProvider
|
||||
locale={locale}
|
||||
messages={messages}
|
||||
timeZone="Europe/Istanbul" // Configure a default timezone for Turkey
|
||||
>
|
||||
<TranslatedUI namespace={namespace} />
|
||||
</NextIntlClientProvider>
|
||||
);
|
||||
}
|
||||
|
||||
function TranslatedUI({ namespace }: { namespace: NamespaceType }) {
|
||||
const t = useTranslations(namespace);
|
||||
|
||||
return (
|
||||
<main>
|
||||
<h1>{t('title')}</h1>
|
||||
<p>{t('description')}</p>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
21
ServicesFrontEnd/frontend/src/i18n.ts
Normal file
21
ServicesFrontEnd/frontend/src/i18n.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import {getRequestConfig} from 'next-intl/server';
|
||||
import {notFound} from 'next/navigation';
|
||||
|
||||
export const locales = ['en', 'tr'];
|
||||
export const defaultLocale = 'tr';
|
||||
|
||||
export function isValidLocale(locale: string): boolean {
|
||||
return locales.includes(locale);
|
||||
}
|
||||
|
||||
export default getRequestConfig(async ({locale}) => {
|
||||
// Validate that the locale is supported
|
||||
if (!locale || !isValidLocale(locale)) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
return {
|
||||
messages: (await import(`../messages/${locale}.json`)).default,
|
||||
locale: locale
|
||||
};
|
||||
});
|
||||
21
ServicesFrontEnd/frontend/src/i18n/en.json
Normal file
21
ServicesFrontEnd/frontend/src/i18n/en.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"Index": {
|
||||
"title": "Hello world!",
|
||||
"description": "This is a simple example",
|
||||
"auth": {
|
||||
"title": "Authentication",
|
||||
"login": "Login",
|
||||
"register": "Register"
|
||||
},
|
||||
"navigation": {
|
||||
"title": "Navigation",
|
||||
"home": "Home",
|
||||
"about": "About",
|
||||
"contact": "Contact",
|
||||
"current": "Current Language"
|
||||
}
|
||||
},
|
||||
"LocaleLayout": {
|
||||
"title": "My Application"
|
||||
}
|
||||
}
|
||||
1
ServicesFrontEnd/frontend/src/i18n/index.ts
Normal file
1
ServicesFrontEnd/frontend/src/i18n/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./routing";
|
||||
2
ServicesFrontEnd/frontend/src/i18n/locales.ts
Normal file
2
ServicesFrontEnd/frontend/src/i18n/locales.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export const locales = ["en", "tr"] as const;
|
||||
export type Locale = (typeof locales)[number];
|
||||
21
ServicesFrontEnd/frontend/src/i18n/messages.ts
Normal file
21
ServicesFrontEnd/frontend/src/i18n/messages.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { getRequestConfig } from "next-intl/server";
|
||||
import { Locale, locales } from "./locales";
|
||||
|
||||
export default getRequestConfig(async ({ locale }) => {
|
||||
// Handle the case where locale might be undefined
|
||||
const safeLocale = locale && locales.includes(locale as Locale)
|
||||
? locale
|
||||
: "tr"; // Default to Turkish if locale is undefined or invalid
|
||||
|
||||
// Load messages for the locale
|
||||
const messages = (await import(`./${safeLocale}.json`)).default;
|
||||
|
||||
// Return both locale and messages as required by RequestConfig
|
||||
return {
|
||||
locale: safeLocale as Locale,
|
||||
messages,
|
||||
// You can add other config options here if needed
|
||||
// timeZone: 'Europe/Istanbul',
|
||||
// now: new Date(),
|
||||
};
|
||||
});
|
||||
7
ServicesFrontEnd/frontend/src/i18n/navigation.ts
Normal file
7
ServicesFrontEnd/frontend/src/i18n/navigation.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import {createNavigation} from 'next-intl/navigation';
|
||||
import {routing} from './routing';
|
||||
|
||||
// Lightweight wrappers around Next.js' navigation
|
||||
// APIs that consider the routing configuration
|
||||
export const {Link, redirect, usePathname, useRouter, getPathname} =
|
||||
createNavigation(routing);
|
||||
138
ServicesFrontEnd/frontend/src/i18n/routing.ts
Normal file
138
ServicesFrontEnd/frontend/src/i18n/routing.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
"use client";
|
||||
import { locales, Locale } from "./locales";
|
||||
import {
|
||||
usePathname as useNextPathname,
|
||||
useRouter as useNextRouter,
|
||||
} from "next/navigation";
|
||||
import { useParams } from "next/navigation";
|
||||
import React from "react";
|
||||
import Link from "next/link";
|
||||
|
||||
// Enhanced Link component for i18n support
|
||||
export const LocaleLink: React.FC<{
|
||||
href: string;
|
||||
locale?: Locale;
|
||||
children: React.ReactNode;
|
||||
}> = ({ href, locale, children }) => {
|
||||
const params = useParams();
|
||||
const currentLocale =
|
||||
locale || (params?.locale as Locale) || routing.defaultLocale;
|
||||
|
||||
// Construct the localized path
|
||||
const path = getLocalizedPath(href, currentLocale);
|
||||
|
||||
// Use a different approach without JSX since this is a .ts file
|
||||
return React.createElement(
|
||||
Link,
|
||||
{ href: `/${currentLocale}${path}` },
|
||||
children
|
||||
);
|
||||
};
|
||||
|
||||
// Helper to get localized path
|
||||
function getLocalizedPath(path: string, locale: Locale): string {
|
||||
// First check if this is a direct path in our pathnames
|
||||
const pathnameConfig = pathnames[path];
|
||||
|
||||
if (typeof pathnameConfig === "string") {
|
||||
return pathnameConfig;
|
||||
}
|
||||
|
||||
if (
|
||||
pathnameConfig &&
|
||||
typeof pathnameConfig === "object" &&
|
||||
locale in pathnameConfig
|
||||
) {
|
||||
return (pathnameConfig as Record<Locale, string>)[locale];
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(pathnames)) {
|
||||
if (typeof value === "object") {
|
||||
const localizedValues = value as Record<Locale, string>;
|
||||
if (localizedValues[locale] === path) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
// Redirect function for server-side redirects
|
||||
export const redirect = (url: string) => {
|
||||
// This is a placeholder for server components
|
||||
// In real implementation, you would use Next.js redirect
|
||||
if (typeof window !== "undefined") {
|
||||
window.location.href = url;
|
||||
}
|
||||
};
|
||||
|
||||
// Enhanced usePathname for i18n
|
||||
export const usePathname = () => {
|
||||
const nextPathname = useNextPathname();
|
||||
const params = useParams();
|
||||
const locale = params?.locale as Locale;
|
||||
|
||||
// Remove locale prefix from pathname
|
||||
if (locale && nextPathname.startsWith(`/${locale}`)) {
|
||||
return nextPathname.substring(`/${locale}`.length) || "/";
|
||||
}
|
||||
|
||||
return nextPathname;
|
||||
};
|
||||
|
||||
// Enhanced useRouter for i18n
|
||||
export const useRouter = () => {
|
||||
const router = useNextRouter();
|
||||
const params = useParams();
|
||||
const locale = (params?.locale as Locale) || routing.defaultLocale;
|
||||
|
||||
return {
|
||||
// Preserve original router methods
|
||||
...router,
|
||||
|
||||
// Enhanced push method with locale handling
|
||||
push: (url: string) => {
|
||||
// Keep the original URL path and just add the locale prefix
|
||||
const fullPath = `/${locale}${url}`;
|
||||
console.log(`Navigating to: ${fullPath}`);
|
||||
|
||||
// Use window.location for more reliable navigation in client components
|
||||
if (typeof window !== "undefined") {
|
||||
window.location.href = fullPath;
|
||||
return;
|
||||
}
|
||||
|
||||
// Fallback to Next.js router
|
||||
router.push(fullPath);
|
||||
},
|
||||
|
||||
// Enhanced replace method with locale handling
|
||||
replace: (url: string) => {
|
||||
// Keep the original URL path and just add the locale prefix
|
||||
const fullPath = `/${locale}${url}`;
|
||||
console.log(`Replacing with: ${fullPath}`);
|
||||
|
||||
// Use window.location for more reliable navigation in client components
|
||||
if (typeof window !== "undefined") {
|
||||
window.location.replace(fullPath);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fallback to Next.js router
|
||||
router.replace(fullPath);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
// Define routes mapping
|
||||
export const pathnames: Record<string, string | Record<Locale, string>> = {
|
||||
"/": "/",
|
||||
"/about": "/about",
|
||||
};
|
||||
|
||||
// Export routing configuration
|
||||
export const routing = {
|
||||
locales,
|
||||
defaultLocale: "tr" as const,
|
||||
};
|
||||
21
ServicesFrontEnd/frontend/src/i18n/tr.json
Normal file
21
ServicesFrontEnd/frontend/src/i18n/tr.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"Index": {
|
||||
"title": "Merhaba dünya!",
|
||||
"description": "Bu basit bir örnektir",
|
||||
"auth": {
|
||||
"title": "Kimlik Doğrulama",
|
||||
"login": "Giriş Yap",
|
||||
"register": "Kayıt Ol"
|
||||
},
|
||||
"navigation": {
|
||||
"title": "Navigasyon",
|
||||
"home": "Ana Sayfa",
|
||||
"about": "Hakkında",
|
||||
"contact": "İletişim",
|
||||
"current": "Mevcut Dili"
|
||||
}
|
||||
},
|
||||
"LocaleLayout": {
|
||||
"title": "Uygulamam"
|
||||
}
|
||||
}
|
||||
3
ServicesFrontEnd/frontend/src/messages/en.json
Normal file
3
ServicesFrontEnd/frontend/src/messages/en.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"title": "Welcome Ninja"
|
||||
}
|
||||
3
ServicesFrontEnd/frontend/src/messages/tr.json
Normal file
3
ServicesFrontEnd/frontend/src/messages/tr.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"title": "Hoşgeldiniz Ninja"
|
||||
}
|
||||
12
ServicesFrontEnd/frontend/src/types/next-intl.d.ts
vendored
Normal file
12
ServicesFrontEnd/frontend/src/types/next-intl.d.ts
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
import { routing } from "@/i18n/routing";
|
||||
import enMessages from "@/i18n/en.json";
|
||||
|
||||
declare module "next-intl" {
|
||||
interface AppConfig {
|
||||
// Type-safe locale
|
||||
Locale: (typeof routing.locales)[number];
|
||||
|
||||
// Type-safe messages
|
||||
Messages: typeof enMessages;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user