diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a733bb0..78c5aa3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,6 +9,7 @@ Thank you for your interest in contributing to this project! This guide will hel - [Tech Stack](#tech-stack) - [Project Structure](#project-structure) - [Development Workflow](#development-workflow) +- [Content Workflows](#content-workflows) - [Code Style & Standards](#code-style--standards) - [Testing](#testing) - [Commit Guidelines](#commit-guidelines) @@ -215,6 +216,413 @@ npm run build Ensure the build completes without errors before submitting. +## Content Workflows + +This section covers how to add and manage content for portfolio projects, blog posts, and translations. The project supports multi-language content in German (de), English (en), and Serbian (sr). + +### Adding Portfolio Projects + +Portfolio projects are defined as TypeScript files with structured metadata and content. Each project must be created for all supported locales. + +#### File Location Conventions + +Portfolio project files follow this structure: +``` +src/i18n/locales-old/ +├── de/portfolio/projects/ # German portfolio projects +│ └── my-project.ts +├── en/portfolio/projects/ # English portfolio projects +│ └── my-project.ts +└── sr/portfolio/projects/ # Serbian portfolio projects + └── my-project.ts +``` + +**Important:** Use the same filename (slug) across all locales to maintain consistency. + +#### TypeScript Data Structure + +Each portfolio project file must export an object with the following structure: + +```typescript +// src/i18n/locales-old/de/portfolio/projects/my-project.ts +export const myProject = { + meta: { + slug: 'my-project', // Unique identifier (same across locales) + title: 'Project Title | Damjan Savić', // SEO-optimized title + description: 'Brief project description for meta tags', + excerpt: 'Short excerpt for project cards', + date: '2025-01', // Publication date (YYYY-MM format) + category: 'Category Name', // e.g., "Web Development", "Data Processing" + client: 'Client Name', // Client or organization + duration: '2 Monate', // Project duration + url: 'https://project-url.com', // Live project URL (optional) + repository: '', // GitHub repository URL (optional) + documentation: '', // Documentation URL (optional) + published: true, // Visibility flag + featured: true, // Featured on homepage + technologies: [ // Technology stack + 'TypeScript', + 'React', + 'Next.js' + ], + tags: [ // Project tags for filtering + 'Web Development', + 'Full Stack', + 'SEO' + ] + }, + content: { + intro: 'Introduction paragraph describing the project overview.', + + challenge: { + title: 'The Challenge', + description: 'Brief description of the problem to solve.', + points: [ + 'Challenge point 1', + 'Challenge point 2', + 'Challenge point 3' + ] + }, + + solution: { + title: 'The Solution', + description: 'How the problem was approached.', + content: 'Detailed explanation of the solution.', + points: [ + 'Solution feature 1', + 'Solution feature 2', + 'Solution feature 3' + ] + }, + + technical: { + title: 'Technical Implementation', + description: 'Technical approach and architecture.', + points: [ + 'Technical detail 1', + 'Technical detail 2', + 'Technical detail 3' + ], + code: `// Optional code snippet to showcase implementation +export function exampleFunction() { + return 'Example code'; +}` + }, + + implementation: { + title: 'Implementation Process', + description: 'How the project was built.', + points: [ + 'Implementation step 1', + 'Implementation step 2', + 'Implementation step 3' + ] + }, + + results: { + title: 'Results and Impact', + description: 'Measurable outcomes and business impact.', + points: [ + 'Result metric 1', + 'Result metric 2', + 'Result metric 3' + ] + }, + + conclusion: 'Final thoughts and project summary.' + } +}; + +// Re-export for compatibility with the project import system +export default myProject; +``` + +#### Step-by-Step: Adding a New Portfolio Project + +1. **Create the TypeScript file** for each locale: + ```bash + # German version + touch src/i18n/locales-old/de/portfolio/projects/my-project.ts + + # English version + touch src/i18n/locales-old/en/portfolio/projects/my-project.ts + + # Serbian version + touch src/i18n/locales-old/sr/portfolio/projects/my-project.ts + ``` + +2. **Copy the template structure** above into each file + +3. **Translate the content** for each locale while keeping the same structure + +4. **Add project images** (if any) to `public/images/portfolio/my-project/` + +5. **Test the project** by navigating to: + - German: `http://localhost:3000/de/portfolio/my-project` + - English: `http://localhost:3000/en/portfolio/my-project` + - Serbian: `http://localhost:3000/sr/portfolio/my-project` + +6. **Verify SEO metadata** using browser DevTools to check meta tags + +### Adding Blog Posts + +Blog posts use MDX (Markdown + JSX) format with frontmatter metadata. MDX allows you to embed React components directly in your content. + +#### File Location Conventions + +Blog posts are organized by locale: +``` +src/content/blog/ +├── de/ # German blog posts +│ └── my-blog-post.mdx +├── en/ # English blog posts +│ └── my-blog-post.mdx +└── sr/ # Serbian blog posts + └── my-blog-post.mdx +``` + +#### MDX File Structure + +Each blog post must include frontmatter metadata at the top: + +```mdx +--- +title: "Blog Post Title" +description: "Brief description for SEO and post previews" +date: "2025-01-20" +author: "Damjan Savić" +category: "Technology" +tags: ["React", "Next.js", "TypeScript"] +image: "/images/blog/my-blog-post/hero.jpg" +published: true +featured: false +excerpt: "A short excerpt that appears in post listings" +--- + +# Blog Post Title + +Your content starts here. You can use standard Markdown syntax: + +## Headings + +Regular **bold** and *italic* text. + +### Code Blocks + +```typescript +// Code examples with syntax highlighting +const greeting = "Hello, World!"; +console.log(greeting); +``` + +### Lists + +- Bullet point 1 +- Bullet point 2 +- Bullet point 3 + +### Images + +![Alt text](/images/blog/my-blog-post/diagram.png) + +### Custom Components (MDX Feature) + +You can embed React components: + + + This is a custom component in MDX! + +``` + +#### Step-by-Step: Adding a New Blog Post + +1. **Create the MDX file** for each locale: + ```bash + # German version + touch src/content/blog/de/my-blog-post.mdx + + # English version + touch src/content/blog/en/my-blog-post.mdx + + # Serbian version + touch src/content/blog/sr/my-blog-post.mdx + ``` + +2. **Add frontmatter metadata** with all required fields + +3. **Write the content** using Markdown/MDX syntax + +4. **Add images** to `public/images/blog/my-blog-post/` + +5. **Generate blog images** (optional) using the AI-powered script: + ```bash + # Preview image generation + npm run generate:images:dry + + # Generate actual images + npm run generate:images + ``` + +6. **Test the blog post** by navigating to: + - German: `http://localhost:3000/de/blog/my-blog-post` + - English: `http://localhost:3000/en/blog/my-blog-post` + - Serbian: `http://localhost:3000/sr/blog/my-blog-post` + +### Internationalization (i18n) Workflow + +The project uses **next-intl** for internationalization with JSON-based translation files. + +#### Translation File Structure + +Translation files are located in `src/messages/` and organized by locale: + +``` +src/messages/ +├── de.json # German translations +├── en.json # English translations +└── sr.json # Serbian translations +``` + +#### Translation File Format + +Each translation file is a nested JSON object organized by page or section: + +```json +{ + "meta": { + "site": { + "name": "Damjan Savić", + "title": "Damjan Savić | Fullstack Developer", + "description": "Portfolio website description" + } + }, + "common": { + "nav": { + "home": "Home", + "about": "About", + "portfolio": "Portfolio", + "blog": "Blog", + "contact": "Contact" + }, + "actions": { + "readMore": "Read More", + "viewMore": "View More", + "back": "Back", + "close": "Close" + } + }, + "pages": { + "home": { + "hero": { + "title": "FULLSTACK DEVELOPER", + "subtitle": "AI Agents | Voice AI | Process Automation" + } + } + } +} +``` + +#### Step-by-Step: Adding Translations + +1. **Add the translation key** to `src/messages/de.json` (German - primary language): + ```json + { + "pages": { + "contact": { + "form": { + "title": "Kontakt aufnehmen", + "name": "Name", + "email": "E-Mail", + "message": "Nachricht", + "submit": "Absenden" + } + } + } + } + ``` + +2. **Add the same structure** to `src/messages/en.json` (English): + ```json + { + "pages": { + "contact": { + "form": { + "title": "Get in Touch", + "name": "Name", + "email": "Email", + "message": "Message", + "submit": "Submit" + } + } + } + } + ``` + +3. **Add the same structure** to `src/messages/sr.json` (Serbian): + ```json + { + "pages": { + "contact": { + "form": { + "title": "Kontaktirajte nas", + "name": "Ime", + "email": "Email", + "message": "Poruka", + "submit": "Pošalji" + } + } + } + } + ``` + +4. **Use the translation** in your component: + ```typescript + import { useTranslations } from 'next-intl'; + + export function ContactForm() { + const t = useTranslations('pages.contact.form'); + + return ( +
+

{t('title')}

+ + +