Skip to content

Commit

Permalink
next-intl localization (#1)
Browse files Browse the repository at this point in the history
* move components into src/components

* setup next-intl, [locale] dir, adapt routing, support localized mdx

* localize content.js

* fix root layout metadata and sitemap

* revamp content handling, localePrefix as needed

* Move i18n settings to config file

* Remove premade content and config template files

* remove tags and variables from subline string

* add favicon and minor code refacts

* remove unused code

* add translation for buttons

* conditionally render language selector in header

* update readme

---------

Co-authored-by: Zsofia <[email protected]>
Co-authored-by: Lorant <[email protected]>
  • Loading branch information
3 people authored Oct 10, 2024
1 parent e1adcb9 commit 6dcdad2
Show file tree
Hide file tree
Showing 61 changed files with 1,162 additions and 885 deletions.
23 changes: 13 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,18 @@ src/app/resources/config

**5. Edit content**
```
src/app/resources/content
src/app/resources/content (or content-i18n for localization)
```

**6. Create blog posts / projects**
```
Add a new .mdx file to src/app/blog/posts or src/app/work/projects
Add a new .mdx file to src/app/[locale]/blog/posts or src/app/[locale]/work/projects
```

# **Features**

## **Once UI**
- All tokens, components & features of [Once UI](https://once-ui.com) (v0.3.1)
- All tokens, components & features of [Once UI](https://once-ui.com)

## **SEO**
- Automatic open-graph and X image generation with next/og
Expand All @@ -46,25 +46,28 @@ Add a new .mdx file to src/app/blog/posts or src/app/work/projects
- Timeless design without heavy animations and motion
- Endless customization options through [data attributes](https://once-ui.com/docs/theming)

TIP:
You try pre-built designs by changing the imports for the config and content in src/app/resources/index.ts

## **Content**
- Render sections conditionally based on the content file
- Enable or disable pages for blog, work, gallery and about / CV
- Generate and display social links automatically
- Set up password protection for URLs

## **Localization (NEW)**
- Magic Portfolio now supports localization with the next-intl library
- See more info in resources/config.js

# **Authors**

Connect with us on X or LinkedIn.
Connect with us on Threads or LinkedIn.

Lorant Toth: [Threads](https://www.threads.net/@lorant.one), [LinkedIn](https://www.linkedin.com/in/tothlorant/)
Zsofia Komaromi: [Threads](https://www.threads.net/@zsofia_kom), [LinkedIn](https://www.linkedin.com/in/zsofiakomaromi/)

Lorant Toth: [X](https://x.com/lorant_one), [LinkedIn](https://www.linkedin.com/in/tothlorant/)
Zsofia Komaromi: [X](https://x.com/zsofiakomaromi), [LinkedIn](https://www.linkedin.com/in/zsofiakomaromi/)
Localization added by [François Hernandez](https://github.com/francoishernandez)

# **Get involved**

- Join the [Once UI Discord server](https://discord.com/invite/5EyAQ4eNdS) and share your portfolio with designers and developers!
- Join the [Design Engineers Club on Discord](https://discord.com/invite/5EyAQ4eNdS) and share your portfolio with us!
- Report a [bug](https://github.com/once-ui-system/magic-portfolio/issues/new?labels=bug&template=bug_report.md).

# **License**
Expand Down
81 changes: 81 additions & 0 deletions messages/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
{
"person": {
"role": "Design Engineer"
},
"newsletter": {
"title": "Subscribe to {firstName}'s Newsletter",
"description": "I occasionally write about design, technology, and share thoughts on the intersection of creativity and engineering",
"button": "Sign up"
},
"home": {
"label": "Home",
"title": "{name}'s Portfolio",
"description": "Portfolio website showcasing my work as a {role}",
"headline": "Design engineer and builder",
"subline": "I'm Selene, a design engineer at FLY, where I craft intuitive user experiences. After hours, I build my own projects."
},
"about": {
"label": "About",
"title": "About me",
"description": "Meet {name}, {role} from {location}",
"intro": {
"title": "Introduction",
"description": "Selene is a Jakarta-based design engineer with a passion for transforming complex challenges into simple, elegant design solutions. Her work spans digital interfaces, interactive experiences, and the convergence of design and technology."
},
"work": {
"title": "Work Experience",
"experiences": {
"FLY": {
"timeframe": "2022 - Present",
"role": "Senior Design Engineer",
"achievements": "Redesigned the UI/UX for the FLY platform, resulting in a 20% increase in user engagement and 30% faster load times.;Spearheaded the integration of AI tools into design workflows, enabling designers to iterate 50% faster."
},
"Creativ3": {
"timeframe": "2018 - 2022",
"role": "Lead Designer",
"achievements": "Developed a design system that unified the brand across multiple platforms, improving design consistency by 40%.;Led a cross-functional team to launch a new product line, contributing to a 15% increase in overall company revenue."
}
}
},
"studies": {
"title": "Studies",
"institutions": {
"University of Jakarta": {
"description": "Studied software engineering."
},
"Build the Future": {
"description": "Studied online marketing and personal branding."
}
}
},
"technical": {
"title": "Technical skills",
"skills": {
"Figma": {
"description": "Able to prototype in Figma with Once UI with unnatural speed."
},
"Nextjs": {
"description": "Building next gen apps with Next.js + Once UI + Supabase."
}
}
}
},
"blog": {
"label": "Blog",
"title": "Writing about design and tech...",
"description": "Read what {name} has been up to recently"
},
"work": {
"label": "Work",
"title": "My projects",
"description": "Design and dev projects by {name}"
},
"gallery": {
"label": "Gallery",
"title": "My photo gallery",
"description": "A photo collection by {name}"
},
"projectCard": {
"label": "Read Case Study"
}
}
81 changes: 81 additions & 0 deletions messages/id.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
{
"person": {
"role": "Insinyur Desain"
},
"newsletter": {
"title": "Langganan Buletin {firstName}",
"description": "Saya sesekali menulis tentang desain, teknologi, dan berbagi pemikiran tentang persimpangan kreativitas dan rekayasa",
"button": "Daftar"
},
"home": {
"label": "Beranda",
"title": "Portofolio {name}",
"description": "Situs web portofolio yang menampilkan karya saya sebagai {role}",
"headline": "Insinyur desain dan pembangun",
"subline": "Saya Selene, seorang insinyur desain di FLY, di mana saya membuat pengalaman pengguna yang intuitif. Setelah jam kerja, saya mengerjakan proyek-proyek pribadi."
},
"about": {
"label": "Tentang",
"title": "Tentang Saya",
"description": "Kenali {name}, {role} dari {location}",
"intro": {
"title": "Pengantar",
"description": "Selene adalah seorang insinyur desain yang berbasis di Jakarta dengan hasrat untuk mengubah tantangan kompleks menjadi solusi desain yang sederhana dan elegan. Karyanya meliputi antarmuka digital, pengalaman interaktif, dan pertemuan antara desain dan teknologi."
},
"work": {
"title": "Pengalaman Kerja",
"experiences": {
"FLY": {
"timeframe": "2022 - Sekarang",
"role": "Senior Design Engineer",
"achievements": "Mendesain ulang UI/UX untuk platform FLY, menghasilkan peningkatan keterlibatan pengguna sebesar 20% dan waktu muat lebih cepat sebesar 30%.;Memimpin integrasi alat AI ke dalam alur kerja desain, memungkinkan desainer untuk iterasi 50% lebih cepat."
},
"Creativ3": {
"timeframe": "2018 - 2022",
"role": "Desainer Utama",
"achievements": "Mengembangkan sistem desain yang menyatukan merek di berbagai platform, meningkatkan konsistensi desain sebesar 40%.;Memimpin tim lintas fungsi untuk meluncurkan lini produk baru, berkontribusi pada peningkatan pendapatan perusahaan sebesar 15%."
}
}
},
"studies": {
"title": "Pendidikan",
"institutions": {
"University of Jakarta": {
"description": "Belajar teknik perangkat lunak."
},
"Build the Future": {
"description": "Belajar pemasaran online dan personal branding."
}
}
},
"technical": {
"title": "Kemampuan Teknis",
"skills": {
"Figma": {
"description": "Mampu membuat prototipe di Figma dengan Once UI dengan kecepatan luar biasa."
},
"Nextjs": {
"description": "Membangun aplikasi generasi berikutnya dengan Next.js + Once UI + Supabase."
}
}
}
},
"blog": {
"label": "Blog",
"title": "Menulis tentang desain dan teknologi...",
"description": "Baca apa yang {name} lakukan baru-baru ini"
},
"work": {
"label": "Karya",
"title": "Proyek Saya",
"description": "Proyek desain dan pengembangan oleh {name}"
},
"gallery": {
"label": "Galeri",
"title": "Galeri Foto Saya",
"description": "Koleksi foto oleh {name}"
},
"projectCard": {
"label": "Baca Studi Kasus"
}
}
5 changes: 4 additions & 1 deletion next.config.mjs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import mdx from '@next/mdx';
import createNextIntlPlugin from 'next-intl/plugin';

const withMDX = mdx({
extension: /\.mdx?$/,
options: { },
});

const withNextIntl = createNextIntlPlugin();

/** @type {import('next').NextConfig} */
const nextConfig = {
pageExtensions: ['ts', 'tsx', 'md', 'mdx'],
};

export default withMDX(nextConfig);
export default withNextIntl(withMDX(nextConfig));
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"cookie": "^0.6.0",
"gray-matter": "^4.0.3",
"next": "^14.2.4",
"next-intl": "^3.20.0",
"next-mdx-remote": "^5.0.0",
"postcss": "^8.4.39",
"postcss-preset-env": "^9.5.15",
Expand Down
70 changes: 40 additions & 30 deletions src/app/about/page.tsx → src/app/[locale]/about/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import { Avatar, Button, Flex, Heading, Icon, IconButton, SmartImage, Tag, Text } from '@/once-ui/components';
import { person, about, social, baseURL } from '@/app/resources'
import TableOfContents from '@/app/about/components/TableOfContents';
import styles from '@/app/about/about.module.scss'
import { baseURL, renderContent } from '@/app/resources';
import TableOfContents from '@/components/about/TableOfContents';
import styles from '@/components/about/about.module.scss'
import { getTranslations, unstable_setRequestLocale } from 'next-intl/server';
import { useTranslations } from 'next-intl';

export function generateMetadata() {
export async function generateMetadata(
{params: {locale}}: { params: { locale: string }}
) {
const t = await getTranslations();
const {person, about, social } = renderContent(t);
const title = about.title;
const description = about.description;
const ogImage = `https://${baseURL}/og?title=${encodeURIComponent(title)}`;
Expand All @@ -15,7 +21,7 @@ export function generateMetadata() {
title,
description,
type: 'website',
url: `https://${baseURL}/blog`,
url: `https://${baseURL}/${locale}/blog`,
images: [
{
url: ogImage,
Expand All @@ -32,30 +38,34 @@ export function generateMetadata() {
};
}

const structure = [
{
title: about.intro.title,
display: about.intro.display,
items: []
},
{
title: about.work.title,
display: about.work.display,
items: about.work.experiences.map(experience => experience.company)
},
{
title: about.studies.title,
display: about.studies.display,
items: about.studies.institutions.map(institution => institution.name)
},
{
title: about.technical.title,
display: about.technical.display,
items: about.technical.skills.map(skill => skill.title)
},
]

export default function About() {
export default function About(
{ params: {locale}}: { params: { locale: string }}
) {
unstable_setRequestLocale(locale);
const t = useTranslations();
const {person, about, social } = renderContent(t);
const structure = [
{
title: about.intro.title,
display: about.intro.display,
items: []
},
{
title: about.work.title,
display: about.work.display,
items: about.work.experiences.map(experience => experience.company)
},
{
title: about.studies.title,
display: about.studies.display,
items: about.studies.institutions.map(institution => institution.name)
},
{
title: about.technical.title,
display: about.technical.display,
items: about.technical.skills.map(skill => skill.title)
},
]
return (
<Flex
fillWidth maxWidth="m"
Expand Down Expand Up @@ -242,7 +252,7 @@ export default function About() {
<Flex
as="ul"
direction="column" gap="16">
{experience.achievements.map((achievement, index) => (
{experience.achievements.map((achievement: string, index: any) => (
<Text
as="li"
variant="body-default-m"
Expand Down
Loading

0 comments on commit 6dcdad2

Please sign in to comment.