auto-claude: subtask-1-4 - Document content workflows (portfolio projects and blog posts)

- Added comprehensive Content Workflows section to CONTRIBUTING.md
- Documented how to add portfolio projects with TypeScript data structure example
- Documented how to add blog posts using MDX format
- Documented i18n workflow for translations with JSON structure
- Added file location conventions for all locales (de, en, sr)
- Included step-by-step instructions and best practices

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-25 06:36:41 +01:00
co-authored by Claude Sonnet 4.5
parent 5822ceb7c4
commit 65f3a28435
+408
View File
@@ -9,6 +9,7 @@ Thank you for your interest in contributing to this project! This guide will hel
- [Tech Stack](#tech-stack) - [Tech Stack](#tech-stack)
- [Project Structure](#project-structure) - [Project Structure](#project-structure)
- [Development Workflow](#development-workflow) - [Development Workflow](#development-workflow)
- [Content Workflows](#content-workflows)
- [Code Style & Standards](#code-style--standards) - [Code Style & Standards](#code-style--standards)
- [Testing](#testing) - [Testing](#testing)
- [Commit Guidelines](#commit-guidelines) - [Commit Guidelines](#commit-guidelines)
@@ -215,6 +216,413 @@ npm run build
Ensure the build completes without errors before submitting. 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:
<CustomCallout type="info">
This is a custom component in MDX!
</CustomCallout>
```
#### 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 (
<form>
<h2>{t('title')}</h2>
<input placeholder={t('name')} />
<input placeholder={t('email')} />
<textarea placeholder={t('message')} />
<button>{t('submit')}</button>
</form>
);
}
```
#### Translation Best Practices
1. **Maintain structure consistency** - Use the same JSON structure across all locale files
2. **Use semantic keys** - Keys should describe the content, not the translation (e.g., `actions.submit` not `actions.submitButton`)
3. **Organize by feature** - Group related translations under feature namespaces
4. **Test all locales** - Always verify translations in all three languages (de, en, sr)
5. **Avoid hardcoded text** - All user-facing text should come from translation files
6. **Keep translations concise** - Match the tone and length of the original language when possible
#### Locale-Specific File Naming
When creating locale-specific content files, follow these conventions:
| Content Type | File Location | Naming Pattern | Example |
|--------------|---------------|----------------|---------|
| **Portfolio Projects** | `src/i18n/locales-old/[locale]/portfolio/projects/` | `project-slug.ts` | `ai-data-reader.ts` |
| **Blog Posts** | `src/content/blog/[locale]/` | `post-slug.mdx` | `react-best-practices.mdx` |
| **Translations** | `src/messages/` | `[locale].json` | `de.json` |
| **Page Content** | `src/app/[locale]/[page]/` | Standard Next.js routing | `page.tsx` |
#### Switching Between Locales
The application automatically detects the user's locale based on:
1. URL path segment (e.g., `/de/`, `/en/`, `/sr/`)
2. Browser language settings
3. User preference stored in cookies
Users can manually switch languages using the language selector in the navigation menu.
## Code Style & Standards ## Code Style & Standards
### ESLint & Code Linting ### ESLint & Code Linting