Align brand search SEO with Grace Salmoun

هذا الالتزام موجود في:
2026-05-03 16:48:06 +03:00
الأصل bd1bb5c2a8
التزام e6f1c6670b
15 ملفات معدلة مع 101 إضافات و48 حذوفات

عرض الملف

@@ -1,5 +1,5 @@
import type { Metadata } from "next"; import type { Metadata } from "next";
import { notFound } from "next/navigation"; import { notFound, permanentRedirect } from "next/navigation";
import { HomePage } from "@/components/home-page"; import { HomePage } from "@/components/home-page";
import { isLanguage, languages, type Language } from "@/data/portfolio"; import { isLanguage, languages, type Language } from "@/data/portfolio";
import { getHomeMetadata } from "@/data/seo"; import { getHomeMetadata } from "@/data/seo";
@@ -21,5 +21,9 @@ export default function LocalizedHomePage({ params }: { params: { lang: string }
notFound(); notFound();
} }
if (params.lang === "en") {
permanentRedirect("/");
}
return <HomePage language={params.lang as Language} />; return <HomePage language={params.lang as Language} />;
} }

عرض الملف

@@ -1,5 +1,5 @@
import type { Metadata } from "next"; import type { Metadata } from "next";
import { notFound } from "next/navigation"; import { notFound, permanentRedirect } from "next/navigation";
import { ResumePageContent } from "@/components/resume-page-content"; import { ResumePageContent } from "@/components/resume-page-content";
import { isLanguage, languages, type Language } from "@/data/portfolio"; import { isLanguage, languages, type Language } from "@/data/portfolio";
import { getResumeMetadata } from "@/data/seo"; import { getResumeMetadata } from "@/data/seo";
@@ -21,5 +21,9 @@ export default function LocalizedResumePage({ params }: { params: { lang: string
notFound(); notFound();
} }
if (params.lang === "en") {
permanentRedirect("/resume");
}
return <ResumePageContent language={params.lang as Language} />; return <ResumePageContent language={params.lang as Language} />;
} }

عرض الملف

@@ -1,5 +1,12 @@
import { redirect } from "next/navigation"; import type { Metadata } from "next";
import { HomePage } from "@/components/home-page";
import type { Language } from "@/data/portfolio";
import { getHomeMetadata } from "@/data/seo";
const defaultLanguage: Language = "en";
export const metadata: Metadata = getHomeMetadata(defaultLanguage);
export default function RootPage() { export default function RootPage() {
redirect("/en"); return <HomePage language={defaultLanguage} />;
} }

عرض الملف

@@ -1,5 +1,12 @@
import { redirect } from "next/navigation"; import type { Metadata } from "next";
import { ResumePageContent } from "@/components/resume-page-content";
import type { Language } from "@/data/portfolio";
import { getResumeMetadata } from "@/data/seo";
const defaultLanguage: Language = "en";
export const metadata: Metadata = getResumeMetadata(defaultLanguage);
export default function RootResumePage() { export default function RootResumePage() {
redirect("/en/resume"); return <ResumePageContent language={defaultLanguage} />;
} }

عرض الملف

@@ -6,7 +6,7 @@ export default function sitemap(): MetadataRoute.Sitemap {
return [ return [
{ {
url: absoluteUrl("/en"), url: absoluteUrl("/"),
lastModified, lastModified,
changeFrequency: "monthly", changeFrequency: "monthly",
priority: 1, priority: 1,
@@ -18,7 +18,7 @@ export default function sitemap(): MetadataRoute.Sitemap {
priority: 1, priority: 1,
}, },
{ {
url: absoluteUrl("/en/resume"), url: absoluteUrl("/resume"),
lastModified, lastModified,
changeFrequency: "monthly", changeFrequency: "monthly",
priority: 0.8, priority: 0.8,

عرض الملف

@@ -7,6 +7,7 @@ import { SiteShell } from "@/components/site-shell";
import { import {
getBasePath, getBasePath,
getDirection, getDirection,
getResumePath,
portfolioContent, portfolioContent,
resumeFile, resumeFile,
sharedProfile, sharedProfile,
@@ -25,6 +26,7 @@ export function HomePage({ language }: { language: Language }) {
? "لوحة بورتفوليو لمجمع إعلامي ومبنى التلفزيون" ? "لوحة بورتفوليو لمجمع إعلامي ومبنى التلفزيون"
: "Portfolio board for Media Complex and TV Building"; : "Portfolio board for Media Complex and TV Building";
const basePath = getBasePath(language); const basePath = getBasePath(language);
const resumePath = getResumePath(language);
const hasResume = resumeFile.available; const hasResume = resumeFile.available;
const contactCards = [ const contactCards = [
{ {
@@ -91,7 +93,7 @@ export function HomePage({ language }: { language: Language }) {
{t.ui.downloadCv} {t.ui.downloadCv}
</Link> </Link>
) : ( ) : (
<Link href={`${basePath}/resume`} className="button-secondary hero-action-link"> <Link href={resumePath} className="button-secondary hero-action-link">
{t.ui.onlineResume} {t.ui.onlineResume}
</Link> </Link>
)} )}
@@ -322,7 +324,7 @@ export function HomePage({ language }: { language: Language }) {
</div> </div>
<div className="grid gap-5 sm:grid-cols-2"> <div className="grid gap-5 sm:grid-cols-2">
<Link href={`${basePath}/resume`} className="soft-card content-stack-default block"> <Link href={resumePath} className="soft-card content-stack-default block">
<p className="eyebrow-note">{t.ui.onlineResume}</p> <p className="eyebrow-note">{t.ui.onlineResume}</p>
<p className="type-card-title display-face text-[var(--color-ink)]"> <p className="type-card-title display-face text-[var(--color-ink)]">
{t.ui.viewFullResume} {t.ui.viewFullResume}

عرض الملف

@@ -5,18 +5,15 @@ import { usePathname } from "next/navigation";
import { portfolioContent, type Language } from "@/data/portfolio"; import { portfolioContent, type Language } from "@/data/portfolio";
export function LanguageToggle({ language }: { language: Language }) { export function LanguageToggle({ language }: { language: Language }) {
const pathname = usePathname() ?? `/${language}`; const pathname = usePathname() ?? (language === "ar" ? "/ar" : "/");
const nextLanguage = language === "en" ? "ar" : "en"; const nextLanguage = language === "en" ? "ar" : "en";
const { languageLabel, languageToggleAriaLabel } = portfolioContent[language].ui; const { languageLabel, languageToggleAriaLabel } = portfolioContent[language].ui;
const segments = pathname.split("/").filter(Boolean); const segments = pathname.split("/").filter(Boolean);
const contentSegments = segments[0] === "en" || segments[0] === "ar" ? segments.slice(1) : segments;
if (segments[0] === "en" || segments[0] === "ar") { const nextPath =
segments[0] = nextLanguage; nextLanguage === "ar"
} else { ? `/ar${contentSegments.length ? `/${contentSegments.join("/")}` : ""}`
segments.unshift(nextLanguage); : `/${contentSegments.join("/")}` || "/";
}
const nextPath = `/${segments.join("/")}`;
return ( return (
<Link <Link

عرض الملف

@@ -32,7 +32,7 @@ export function Navbar({ language }: { language: Language }) {
<div className="logo-shell navbar-logo-shell overflow-hidden rounded-[20px] border p-2"> <div className="logo-shell navbar-logo-shell overflow-hidden rounded-[20px] border p-2">
<Image <Image
src={sharedProfile.logoImage} src={sharedProfile.logoImage}
alt={language === "ar" ? "شعار غريس بطرس سلمون" : "Grace Butrus Salmoun logo"} alt={language === "ar" ? "شعار غريس بطرس سلمون" : "Grace Salmoun logo"}
width={60} width={60}
height={60} height={60}
className="navbar-logo-image h-[60px] w-[60px] rounded-[14px] object-contain" className="navbar-logo-image h-[60px] w-[60px] rounded-[14px] object-contain"

عرض الملف

@@ -2,7 +2,7 @@ import Link from "next/link";
import { JsonLd } from "@/components/json-ld"; import { JsonLd } from "@/components/json-ld";
import { SectionHeading } from "@/components/section-heading"; import { SectionHeading } from "@/components/section-heading";
import { SiteShell } from "@/components/site-shell"; import { SiteShell } from "@/components/site-shell";
import { portfolioContent, resumeFile, sharedProfile, type Language } from "@/data/portfolio"; import { getBasePath, portfolioContent, resumeFile, sharedProfile, type Language } from "@/data/portfolio";
import { getResumeStructuredData } from "@/data/seo"; import { getResumeStructuredData } from "@/data/seo";
export function ResumePageContent({ language }: { language: Language }) { export function ResumePageContent({ language }: { language: Language }) {
@@ -11,6 +11,7 @@ export function ResumePageContent({ language }: { language: Language }) {
const address = language === "ar" ? sharedProfile.addressAr : sharedProfile.address; const address = language === "ar" ? sharedProfile.addressAr : sharedProfile.address;
const hasResume = resumeFile.available; const hasResume = resumeFile.available;
const structuredData = getResumeStructuredData(language); const structuredData = getResumeStructuredData(language);
const portfolioPath = getBasePath(language);
return ( return (
<SiteShell language={language}> <SiteShell language={language}>
@@ -41,7 +42,7 @@ export function ResumePageContent({ language }: { language: Language }) {
{t.ui.resumeUnavailable} {t.ui.resumeUnavailable}
</p> </p>
)} )}
<Link href={`/${language}`} className="button-secondary"> <Link href={portfolioPath} className="button-secondary">
{t.ui.backToPortfolio} {t.ui.backToPortfolio}
</Link> </Link>
</div> </div>

عرض الملف

@@ -2,7 +2,7 @@ export type { Language, Project, AdditionalProject, PortfolioDictionary } from "
export { sharedProfile, resumeFile, defaultLanguage } from "./portfolio/shared"; export { sharedProfile, resumeFile, defaultLanguage } from "./portfolio/shared";
export { englishPortfolioContent } from "./portfolio/en"; export { englishPortfolioContent } from "./portfolio/en";
export { arabicPortfolioContent } from "./portfolio/ar"; export { arabicPortfolioContent } from "./portfolio/ar";
export { getBasePath, getDirection, getPortfolioContent, isLanguage, languages } from "./portfolio/helpers"; export { getBasePath, getDirection, getPortfolioContent, getResumePath, isLanguage, languages } from "./portfolio/helpers";
import { englishPortfolioContent } from "./portfolio/en"; import { englishPortfolioContent } from "./portfolio/en";
import { arabicPortfolioContent } from "./portfolio/ar"; import { arabicPortfolioContent } from "./portfolio/ar";

عرض الملف

@@ -2,9 +2,9 @@ import type { AdditionalProject, PortfolioDictionary, Project } from "./types";
export const englishPortfolioContent = { export const englishPortfolioContent = {
meta: { meta: {
title: "Grace Butrus Salmoun | Architectural Engineer Specialized in Architectural Design", title: "Grace Salmoun | Architectural Engineer and Architecture Portfolio",
description: description:
"An architecture portfolio for Grace Butrus Salmoun, a Damascus-based architectural engineer specialized in architectural design, featuring work in urban rehabilitation, mixed-use towers, hospitality, landscape, shop drawings, and architectural visualization.", "An architecture portfolio for Grace Salmoun, also known as Grace Butrus Salmoun, a Damascus-based architectural engineer specializing in architectural design, urban rehabilitation, landscape, shop drawings, and architectural visualization.",
}, },
ui: { ui: {
navAbout: "About", navAbout: "About",
@@ -71,7 +71,7 @@ export const englishPortfolioContent = {
title: title:
"An architectural engineer who turns ideas into clear drawings and readable presentation boards.", "An architectural engineer who turns ideas into clear drawings and readable presentation boards.",
description: description:
"Grace Butrus Salmoun is a Syrian architect based in Damascus, holding a Master's degree in Architectural Design from Damascus University, with published academic research in 2025 titled \"A Study of the Factors Influencing the Residential Urban Product: Marota City as a Case Study.\" Her portfolio includes towers, hotels, villas, public squares, landscape studies, and shop drawings.", "Grace Salmoun, also known as Grace Butrus Salmoun, is a Syrian architect based in Damascus, holding a Master's degree in Architectural Design from Damascus University, with published academic research in 2025 titled \"A Study of the Factors Influencing the Residential Urban Product: Marota City as a Case Study.\" Her portfolio includes towers, hotels, villas, public squares, landscape studies, and shop drawings.",
paragraphs: [ paragraphs: [
"Her work connects technical production with architectural presentation through plans, sections, elevations, analytical drawings, and 3D visualizations.", "Her work connects technical production with architectural presentation through plans, sections, elevations, analytical drawings, and 3D visualizations.",
"The portfolio includes professional and academic work in Damascus, a first-place project for the rehabilitation of Bab Touma Square, remote collaboration with Regal Pool, and teaching support at Damascus University.", "The portfolio includes professional and academic work in Damascus, a first-place project for the rehabilitation of Bab Touma Square, remote collaboration with Regal Pool, and teaching support at Damascus University.",
@@ -333,7 +333,7 @@ export const englishPortfolioContent = {
onlineResumeDescription: onlineResumeDescription:
"An architectural engineer with a Master's background in architectural design, published academic research in 2025 on the residential urban product in Marota City, teaching support experience, remote design work, and urban rehabilitation achievements.", "An architectural engineer with a Master's background in architectural design, published academic research in 2025 on the residential urban product in Marota City, teaching support experience, remote design work, and urban rehabilitation achievements.",
resumeIntro: resumeIntro:
"Grace Butrus Salmoun is a Damascus-based architectural engineer specialized in architectural design, working in architectural design, urban rehabilitation, landscape concepts, shop drawings, and visual presentation.", "Grace Salmoun is a Damascus-based architectural engineer specialized in architectural design, working in architectural design, urban rehabilitation, landscape concepts, shop drawings, and visual presentation.",
profileParagraph: profileParagraph:
"She combines organized design thinking, technical drawing ability, 3D modeling, and clear presentation skills, alongside academic research experience connected to her master's thesis and a published study on Marota City, with Arabic as a first language and good proficiency in English and French.", "She combines organized design thinking, technical drawing ability, 3D modeling, and clear presentation skills, alongside academic research experience connected to her master's thesis and a published study on Marota City, with Arabic as a first language and good proficiency in English and French.",
experienceDescription: experienceDescription:
@@ -381,5 +381,5 @@ export const englishPortfolioContent = {
{ value: "3", label: "Languages: Arabic, English, and French" }, { value: "3", label: "Languages: Arabic, English, and French" },
], ],
footer: footer:
"Architectural design, urban rehabilitation, landscape studies, shop drawings, and visual presentation by Grace Butrus Salmoun.", "Architectural design, urban rehabilitation, landscape studies, shop drawings, and visual presentation by Grace Salmoun.",
} satisfies PortfolioDictionary; } satisfies PortfolioDictionary;

عرض الملف

@@ -13,7 +13,11 @@ export function getDirection(language: Language) {
} }
export function getBasePath(language: Language) { export function getBasePath(language: Language) {
return `/${language}`; return language === "ar" ? "/ar" : "/";
}
export function getResumePath(language: Language) {
return language === "ar" ? "/ar/resume" : "/resume";
} }
export function getPortfolioContent(language: Language): PortfolioDictionary { export function getPortfolioContent(language: Language): PortfolioDictionary {

عرض الملف

@@ -1,9 +1,11 @@
import type { Language } from "./types"; import type { Language } from "./types";
export const sharedProfile = { export const sharedProfile = {
brandNameEn: "Grace Butrus Salmoun", brandNameEn: "Grace Salmoun",
brandNameAr: "غريس بطرس سلمون", brandNameAr: "غريس بطرس سلمون",
founderNameEn: "Grace Butrus Salmoun", founderNameEn: "Grace Salmoun",
fullNameEn: "Grace Butrus Salmoun",
alternateNamesEn: ["Grace Butrus Salmoun", "Grace Salamoun"],
founderNameAr: "غريس بطرس سلمون", founderNameAr: "غريس بطرس سلمون",
phone: "+963 993 292 652", phone: "+963 993 292 652",
whatsappHref: "https://wa.me/963993292652", whatsappHref: "https://wa.me/963993292652",

عرض الملف

@@ -2,7 +2,15 @@ import type { Metadata } from "next";
import { englishPortfolioContent, getPortfolioContent, sharedProfile, type Language } from "@/data/portfolio"; import { englishPortfolioContent, getPortfolioContent, sharedProfile, type Language } from "@/data/portfolio";
import { absoluteUrl, siteName } from "@/data/site-config"; import { absoluteUrl, siteName } from "@/data/site-config";
const primaryEnglishName = sharedProfile.founderNameEn;
const fullEnglishName = sharedProfile.fullNameEn;
const englishAlternateNames = sharedProfile.alternateNamesEn;
const arabicName = sharedProfile.founderNameAr;
const sharedKeywords = [ const sharedKeywords = [
"Grace Salmoun",
"Grace Butrus Salmoun",
"Grace Salamoun",
"architecture portfolio", "architecture portfolio",
"architectural engineer", "architectural engineer",
"architectural design", "architectural design",
@@ -26,13 +34,21 @@ function getAlternateLocales(language: Language) {
return language === "ar" ? ["en_US"] : ["ar_SY"]; return language === "ar" ? ["en_US"] : ["ar_SY"];
} }
function getHomePath(language: Language) {
return language === "ar" ? "/ar" : "/";
}
function getResumePath(language: Language) {
return language === "ar" ? "/ar/resume" : "/resume";
}
function getOgImage(language: Language) { function getOgImage(language: Language) {
return { return {
url: absoluteUrl(sharedProfile.heroImage), url: absoluteUrl(sharedProfile.heroImage),
alt: alt:
language === "ar" language === "ar"
? "معاينة بورتفوليو غريس بطرس سلمون" ? "معاينة بورتفوليو غريس بطرس سلمون"
: "Portfolio preview for Grace Butrus Salmoun", : "Portfolio preview for Grace Salmoun",
}; };
} }
@@ -55,16 +71,16 @@ function buildMetadata({
title, title,
description, description,
keywords: [...sharedKeywords, ...localizedKeywords[language]], keywords: [...sharedKeywords, ...localizedKeywords[language]],
authors: [{ name: sharedProfile.founderNameEn }], authors: [{ name: primaryEnglishName }],
creator: sharedProfile.founderNameEn, creator: primaryEnglishName,
publisher: sharedProfile.founderNameEn, publisher: primaryEnglishName,
category: "architecture portfolio", category: "architecture portfolio",
alternates: { alternates: {
canonical: canonicalPath, canonical: canonicalPath,
languages: { languages: {
en: canonicalPath.includes("/resume") ? "/en/resume" : "/en", en: canonicalPath.includes("/resume") ? "/resume" : "/",
ar: canonicalPath.includes("/resume") ? "/ar/resume" : "/ar", ar: canonicalPath.includes("/resume") ? "/ar/resume" : "/ar",
"x-default": "/en", "x-default": "/",
}, },
}, },
robots: { robots: {
@@ -106,9 +122,9 @@ export function getDefaultSeoMetadata(): Metadata {
title, title,
description, description,
applicationName: siteName, applicationName: siteName,
authors: [{ name: sharedProfile.founderNameEn }], authors: [{ name: primaryEnglishName }],
creator: sharedProfile.founderNameEn, creator: primaryEnglishName,
publisher: sharedProfile.founderNameEn, publisher: primaryEnglishName,
keywords: [...sharedKeywords, ...localizedKeywords.en], keywords: [...sharedKeywords, ...localizedKeywords.en],
category: "architecture portfolio", category: "architecture portfolio",
referrer: "origin-when-cross-origin", referrer: "origin-when-cross-origin",
@@ -126,7 +142,7 @@ export function getDefaultSeoMetadata(): Metadata {
openGraph: { openGraph: {
title, title,
description, description,
url: absoluteUrl("/en"), url: absoluteUrl("/"),
siteName, siteName,
locale: "en_US", locale: "en_US",
alternateLocale: ["ar_SY"], alternateLocale: ["ar_SY"],
@@ -149,7 +165,7 @@ export function getHomeMetadata(language: Language): Metadata {
language, language,
title: t.meta.title, title: t.meta.title,
description: t.meta.description, description: t.meta.description,
canonicalPath: `/${language}`, canonicalPath: getHomePath(language),
pageType: "website", pageType: "website",
}); });
} }
@@ -162,7 +178,7 @@ export function getResumeMetadata(language: Language): Metadata {
language, language,
title, title,
description: t.resume.description, description: t.resume.description,
canonicalPath: `/${language}/resume`, canonicalPath: getResumePath(language),
pageType: "profile", pageType: "profile",
}); });
} }
@@ -177,6 +193,7 @@ export function getHomeStructuredData(language: Language) {
"@context": "https://schema.org", "@context": "https://schema.org",
"@type": "WebSite", "@type": "WebSite",
name: siteName, name: siteName,
alternateName: [`${fullEnglishName} Portfolio`, "Grace Salamoun Portfolio"],
url: absoluteUrl("/"), url: absoluteUrl("/"),
inLanguage: ["en", "ar"], inLanguage: ["en", "ar"],
}, },
@@ -184,7 +201,11 @@ export function getHomeStructuredData(language: Language) {
"@context": "https://schema.org", "@context": "https://schema.org",
"@type": "Person", "@type": "Person",
name, name,
url: absoluteUrl(`/${language}`), givenName: "Grace",
additionalName: "Butrus",
familyName: "Salmoun",
alternateName: language === "ar" ? [primaryEnglishName, fullEnglishName, ...englishAlternateNames] : [fullEnglishName, arabicName, ...englishAlternateNames],
url: absoluteUrl(getHomePath(language)),
image: absoluteUrl(sharedProfile.heroImage), image: absoluteUrl(sharedProfile.heroImage),
jobTitle: t.ui.architectureEngineer, jobTitle: t.ui.architectureEngineer,
description: t.meta.description, description: t.meta.description,
@@ -203,7 +224,7 @@ export function getHomeStructuredData(language: Language) {
"@type": "CollectionPage", "@type": "CollectionPage",
name: t.meta.title, name: t.meta.title,
description: t.meta.description, description: t.meta.description,
url: absoluteUrl(`/${language}`), url: absoluteUrl(getHomePath(language)),
inLanguage: language, inLanguage: language,
isPartOf: { isPartOf: {
"@type": "WebSite", "@type": "WebSite",
@@ -228,7 +249,11 @@ export function getResumeStructuredData(language: Language) {
"@context": "https://schema.org", "@context": "https://schema.org",
"@type": "Person", "@type": "Person",
name, name,
url: absoluteUrl(`/${language}`), givenName: "Grace",
additionalName: "Butrus",
familyName: "Salmoun",
alternateName: language === "ar" ? [primaryEnglishName, fullEnglishName, ...englishAlternateNames] : [fullEnglishName, arabicName, ...englishAlternateNames],
url: absoluteUrl(getHomePath(language)),
email: sharedProfile.email, email: sharedProfile.email,
telephone: sharedProfile.phone, telephone: sharedProfile.phone,
image: absoluteUrl(sharedProfile.heroImage), image: absoluteUrl(sharedProfile.heroImage),
@@ -240,7 +265,7 @@ export function getResumeStructuredData(language: Language) {
"@type": "ProfilePage", "@type": "ProfilePage",
name: title, name: title,
description: t.resume.description, description: t.resume.description,
url: absoluteUrl(`/${language}/resume`), url: absoluteUrl(getResumePath(language)),
inLanguage: language, inLanguage: language,
isPartOf: { isPartOf: {
"@type": "WebSite", "@type": "WebSite",

عرض الملف

@@ -1,6 +1,6 @@
const FALLBACK_SITE_URL = "https://grace-salamoun-architect.vercel.app"; const FALLBACK_SITE_URL = "https://grace-salamoun-architect.vercel.app";
export const siteName = "Grace Butrus Salmoun Portfolio"; export const siteName = "Grace Salmoun Portfolio";
export const siteUrl = (process.env.NEXT_PUBLIC_SITE_URL ?? FALLBACK_SITE_URL).replace(/\/+$/, ""); export const siteUrl = (process.env.NEXT_PUBLIC_SITE_URL ?? FALLBACK_SITE_URL).replace(/\/+$/, "");
export function absoluteUrl(path = "/") { export function absoluteUrl(path = "/") {