Add language dropdown localization

This commit is contained in:
Codex
2026-05-05 00:19:17 +01:00
parent d5c5f4fc17
commit f6ace49f2f
17 changed files with 552 additions and 261 deletions

48
messages/ar.json Normal file
View File

@@ -0,0 +1,48 @@
{
"Meta": {
"title": "Kairas | استوديو تصميم مواقع",
"description": "Kairas شركة تصميم مواقع تصنع مواقع مصقولة وأنظمة علامات ومنتجات رقمية."
},
"Home": {
"nav": {
"studio": "الاستوديو",
"services": "الخدمات",
"work": "الأعمال",
"notes": "ملاحظات"
},
"localeSwitcher": {
"label": "اللغة"
},
"hero": {
"kicker": "شركة تصميم مواقع",
"words": ["مواقع", "بقوة", "هادئة."],
"interfaceLabel": "واجهة مختارة",
"brandLabel": "نظام العلامة",
"interfaceCopy": "تخطيطات مضبوطة لقراءة هادئة وفعل حاسم.",
"ticker": ["استراتيجية", "تصميم", "بناء"],
"copy": "تصمم Kairas وتبني مواقع راقية للمؤسسين والاستوديوهات وعلامات الخدمات التي تهتم بالذوق والوضوح والأداء التجاري."
},
"marquee": ["استراتيجية", "تصميم", "تطوير", "أنظمة", "إطلاق"],
"studio": {
"eyebrow": "من نحن",
"title": "نصنع بيوتاً رقمية للعلامات التي تحتاج أن تبدو دقيقة ومقصودة وجديرة بالثقة.",
"copy": "يقع عملنا بين استوديو التصميم وحرفة الواجهة الأمامية. نستخدم خطاً قوياً وإيقاعاً واسعاً وأنظمة منضبطة ليبدو كل صفحه مركباً من دون جمود."
},
"servicesTitle": "الخدمات",
"work": {
"eyebrow": "أعمال مختارة",
"title": "أماكن تستطيع فيها العلامة أن تتنفس."
},
"process": {
"title": "العملية"
},
"notes": {
"title": "ملاحظات",
"eyebrow": "دفتر الاستوديو"
},
"footer": {
"headline": "ابن الموقع الذي كانت علامتك تنتظره.",
"services": "استراتيجية / تصميم / تطوير"
}
}
}

48
messages/bn.json Normal file
View File

@@ -0,0 +1,48 @@
{
"Meta": {
"title": "Kairas | ওয়েব ডিজাইন স্টুডিও",
"description": "Kairas একটি ওয়েব ডিজাইন প্রতিষ্ঠান, যা পরিশীলিত ওয়েবসাইট, ব্র্যান্ড সিস্টেম এবং ডিজিটাল পণ্য তৈরি করে।"
},
"Home": {
"nav": {
"studio": "স্টুডিও",
"services": "সেবা",
"work": "কাজ",
"notes": "নোট"
},
"localeSwitcher": {
"label": "ভাষা"
},
"hero": {
"kicker": "ওয়েব ডিজাইন প্রতিষ্ঠান",
"words": ["নীরব", "শক্তির", "ওয়েবসাইট।"],
"interfaceLabel": "নির্বাচিত ইন্টারফেস",
"brandLabel": "ব্র্যান্ড সিস্টেম",
"interfaceCopy": "শান্ত পাঠ এবং সিদ্ধান্তমূলক কাজের জন্য সাজানো লেআউট।",
"ticker": ["কৌশল", "ডিজাইন", "নির্মাণ"],
"copy": "Kairas প্রতিষ্ঠাতা, স্টুডিও এবং সেবা ব্র্যান্ডের জন্য পরিশীলিত ওয়েবসাইট ডিজাইন ও তৈরি করে, যেখানে রুচি, স্বচ্ছতা এবং বাণিজ্যিক ফলাফল গুরুত্বপূর্ণ।"
},
"marquee": ["কৌশল", "ডিজাইন", "ডেভেলপমেন্ট", "সিস্টেম", "লঞ্চ"],
"studio": {
"eyebrow": "আমাদের সম্পর্কে",
"title": "আমরা এমন ব্র্যান্ডের জন্য ডিজিটাল ঘর তৈরি করি, যাদের সুনির্দিষ্ট, সচেতন এবং সহজে বিশ্বাসযোগ্য মনে হওয়া দরকার।",
"copy": "আমাদের কাজ ডিজাইন স্টুডিও এবং ফ্রন্ট-এন্ড কারুশিল্পের মাঝামাঝি। শক্তিশালী টাইপোগ্রাফি, উদার গতি এবং শৃঙ্খলাবদ্ধ সিস্টেম প্রতিটি পেজকে সংযত রাখে।"
},
"servicesTitle": "সেবা",
"work": {
"eyebrow": "নির্বাচিত কাজ",
"title": "যেখানে ব্র্যান্ড শ্বাস নিতে পারে।"
},
"process": {
"title": "প্রক্রিয়া"
},
"notes": {
"title": "নোট",
"eyebrow": "স্টুডিও জার্নাল"
},
"footer": {
"headline": "আপনার ব্র্যান্ড যে সাইটটির অপেক্ষায় ছিল, সেটি তৈরি করুন।",
"services": "কৌশল / ডিজাইন / ডেভেলপমেন্ট"
}
}
}

View File

@@ -1,101 +0,0 @@
{
"Meta": {
"title": "Kairas | Web design studio in Ireland",
"description": "Kairas is an Ireland-based web design firm crafting refined websites, brand systems, and digital products."
},
"Home": {
"nav": {
"studio": "Studio",
"services": "Services",
"work": "Work",
"notes": "Notes",
"contact": "hello@kairas.io"
},
"localeSwitcher": {
"label": "Country",
"options": {
"en-ie": "Ireland",
"en-gb": "United Kingdom",
"en-us": "United States"
}
},
"hero": {
"kicker": "Web design firm in Ireland",
"siteUrl": "kairas.io",
"words": ["Websites", "with quiet", "force."],
"interfaceLabel": "Selected interface",
"brandLabel": "Brand system",
"interfaceCopy": "Layouts tuned for calm reading and decisive action.",
"ticker": ["Strategy", "Design", "Build"],
"copy": "Kairas designs and builds refined websites for founders, studios, and service brands across Ireland that care about taste, clarity, and commercial performance."
},
"marquee": ["Strategy", "Design", "Development", "Systems", "Launch"],
"studio": {
"eyebrow": "About us",
"title": "We shape digital homes for Irish brands that need to feel exact, intentional, and easy to trust.",
"copy": "Our work sits between design studio and front-end craft. We use strong typography, generous pacing, and disciplined systems to make every page feel composed without becoming static."
},
"servicesTitle": "Services",
"services": [
{
"number": "01",
"title": "Brand-led websites",
"text": "Identity, UX, interface design, and front-end systems for companies that need their site to carry the weight of the brand."
},
{
"number": "02",
"title": "Editorial product pages",
"text": "Launch pages, case studies, and content structures that make complex offers feel considered, useful, and easy to move through."
},
{
"number": "03",
"title": "Design systems",
"text": "Reusable components, visual rules, and interaction patterns that help teams ship new pages without losing quality."
}
],
"work": {
"eyebrow": "Selected work",
"title": "Places where the brand can breathe.",
"items": [
["Atelier North", "Architecture portfolio", "2026"],
["Vellum Labs", "SaaS website", "2025"],
["Morrow House", "Hospitality booking", "2025"],
["Plainform", "Brand system", "2024"]
]
},
"process": {
"title": "Process",
"steps": [
{
"title": "Read",
"text": "We clarify the offer, audience, proof, and moments where a visitor needs confidence."
},
{
"title": "Compose",
"text": "We design the system: type, pacing, components, motion notes, and content hierarchy."
},
{
"title": "Ship",
"text": "We build responsive pages with practical handoff, analytics, and room to grow."
}
]
},
"notes": {
"title": "Notes",
"eyebrow": "Studio journal",
"items": [
["2026.04.18", "Essay", "Designing quieter conversion paths for premium service brands"],
["2026.03.02", "Studio", "Kairas opens a focused website sprint for early-stage teams"],
["2026.01.14", "Guide", "What belongs above the fold when the work is the proof"]
]
},
"footer": {
"brand": "Kairas",
"headline": "Build the site your brand has been waiting for.",
"siteUrl": "kairas.io",
"email": "hello@kairas.io",
"services": "Strategy / Design / Development",
"copyright": "© 2026 Kairas"
}
}
}

View File

@@ -1,101 +0,0 @@
{
"Meta": {
"title": "Kairas | Web design studio for US teams",
"description": "Kairas is a web design firm crafting refined websites, brand systems, and digital products for US founders and service brands."
},
"Home": {
"nav": {
"studio": "Studio",
"services": "Services",
"work": "Work",
"notes": "Notes",
"contact": "hello@kairas.io"
},
"localeSwitcher": {
"label": "Country",
"options": {
"en-ie": "Ireland",
"en-gb": "United Kingdom",
"en-us": "United States"
}
},
"hero": {
"kicker": "Web design firm for US teams",
"siteUrl": "kairas.io",
"words": ["Websites", "with quiet", "force."],
"interfaceLabel": "Selected interface",
"brandLabel": "Brand system",
"interfaceCopy": "Layouts tuned for calm reading and decisive action.",
"ticker": ["Strategy", "Design", "Build"],
"copy": "Kairas designs and builds refined websites for US founders, studios, and service brands that care about taste, clarity, and commercial performance."
},
"marquee": ["Strategy", "Design", "Development", "Systems", "Launch"],
"studio": {
"eyebrow": "About us",
"title": "We shape digital homes for US teams that need to feel exact, intentional, and easy to trust.",
"copy": "Our work sits between design studio and front-end craft. We use strong typography, generous pacing, and disciplined systems to make every page feel composed without becoming static."
},
"servicesTitle": "Services",
"services": [
{
"number": "01",
"title": "Brand-led websites",
"text": "Identity, UX, interface design, and front-end systems for companies that need their site to carry the weight of the brand."
},
{
"number": "02",
"title": "Editorial product pages",
"text": "Launch pages, case studies, and content structures that make complex offers feel considered, useful, and easy to move through."
},
{
"number": "03",
"title": "Design systems",
"text": "Reusable components, visual rules, and interaction patterns that help teams ship new pages without losing quality."
}
],
"work": {
"eyebrow": "Selected work",
"title": "Places where the brand can breathe.",
"items": [
["Atelier North", "Architecture portfolio", "2026"],
["Vellum Labs", "SaaS website", "2025"],
["Morrow House", "Hospitality booking", "2025"],
["Plainform", "Brand system", "2024"]
]
},
"process": {
"title": "Process",
"steps": [
{
"title": "Read",
"text": "We clarify the offer, audience, proof, and moments where a visitor needs confidence."
},
{
"title": "Compose",
"text": "We design the system: type, pacing, components, motion notes, and content hierarchy."
},
{
"title": "Ship",
"text": "We build responsive pages with practical handoff, analytics, and room to grow."
}
]
},
"notes": {
"title": "Notes",
"eyebrow": "Studio journal",
"items": [
["2026.04.18", "Essay", "Designing quieter conversion paths for premium service brands"],
["2026.03.02", "Studio", "Kairas opens a focused website sprint for early-stage teams"],
["2026.01.14", "Guide", "What belongs above the fold when the work is the proof"]
]
},
"footer": {
"brand": "Kairas",
"headline": "Build the site your brand has been waiting for.",
"siteUrl": "kairas.io",
"email": "hello@kairas.io",
"services": "Strategy / Design / Development",
"copyright": "© 2026 Kairas"
}
}
}

View File

@@ -1,7 +1,7 @@
{ {
"Meta": { "Meta": {
"title": "Kairas | Web design studio for UK brands", "title": "Kairas | Web design studio",
"description": "Kairas is a web design firm crafting refined websites, brand systems, and digital products for UK teams." "description": "Kairas is a web design firm crafting refined websites, brand systems, and digital products."
}, },
"Home": { "Home": {
"nav": { "nav": {
@@ -12,27 +12,22 @@
"contact": "hello@kairas.io" "contact": "hello@kairas.io"
}, },
"localeSwitcher": { "localeSwitcher": {
"label": "Country", "label": "Language"
"options": {
"en-ie": "Ireland",
"en-gb": "United Kingdom",
"en-us": "United States"
}
}, },
"hero": { "hero": {
"kicker": "Web design firm for UK brands", "kicker": "Web design firm",
"siteUrl": "kairas.io", "siteUrl": "kairas.io",
"words": ["Websites", "with quiet", "force."], "words": ["Websites", "with quiet", "force."],
"interfaceLabel": "Selected interface", "interfaceLabel": "Selected interface",
"brandLabel": "Brand system", "brandLabel": "Brand system",
"interfaceCopy": "Layouts tuned for calm reading and decisive action.", "interfaceCopy": "Layouts tuned for calm reading and decisive action.",
"ticker": ["Strategy", "Design", "Build"], "ticker": ["Strategy", "Design", "Build"],
"copy": "Kairas designs and builds refined websites for UK founders, studios, and service brands that care about taste, clarity, and commercial performance." "copy": "Kairas designs and builds refined websites for founders, studios, and service brands that care about taste, clarity, and commercial performance."
}, },
"marquee": ["Strategy", "Design", "Development", "Systems", "Launch"], "marquee": ["Strategy", "Design", "Development", "Systems", "Launch"],
"studio": { "studio": {
"eyebrow": "About us", "eyebrow": "About us",
"title": "We shape digital homes for UK brands that need to feel exact, intentional, and easy to trust.", "title": "We shape digital homes for brands that need to feel exact, intentional, and easy to trust.",
"copy": "Our work sits between design studio and front-end craft. We use strong typography, generous pacing, and disciplined systems to make every page feel composed without becoming static." "copy": "Our work sits between design studio and front-end craft. We use strong typography, generous pacing, and disciplined systems to make every page feel composed without becoming static."
}, },
"servicesTitle": "Services", "servicesTitle": "Services",

48
messages/es.json Normal file
View File

@@ -0,0 +1,48 @@
{
"Meta": {
"title": "Kairas | Estudio de diseño web",
"description": "Kairas es una firma de diseño web que crea sitios refinados, sistemas de marca y productos digitales."
},
"Home": {
"nav": {
"studio": "Estudio",
"services": "Servicios",
"work": "Trabajo",
"notes": "Notas"
},
"localeSwitcher": {
"label": "Idioma"
},
"hero": {
"kicker": "Firma de diseño web",
"words": ["Sitios web", "con fuerza", "silenciosa."],
"interfaceLabel": "Interfaz seleccionada",
"brandLabel": "Sistema de marca",
"interfaceCopy": "Diseños ajustados para una lectura calma y una acción decidida.",
"ticker": ["Estrategia", "Diseño", "Construcción"],
"copy": "Kairas diseña y construye sitios web refinados para fundadores, estudios y marcas de servicios que cuidan el gusto, la claridad y el rendimiento comercial."
},
"marquee": ["Estrategia", "Diseño", "Desarrollo", "Sistemas", "Lanzamiento"],
"studio": {
"eyebrow": "Sobre nosotros",
"title": "Damos forma a hogares digitales para marcas que necesitan sentirse exactas, intencionales y fáciles de confiar.",
"copy": "Nuestro trabajo vive entre el estudio de diseño y el oficio front-end. Usamos tipografía fuerte, ritmo generoso y sistemas disciplinados para que cada página se sienta compuesta sin volverse estática."
},
"servicesTitle": "Servicios",
"work": {
"eyebrow": "Trabajo seleccionado",
"title": "Lugares donde la marca puede respirar."
},
"process": {
"title": "Proceso"
},
"notes": {
"title": "Notas",
"eyebrow": "Diario del estudio"
},
"footer": {
"headline": "Construye el sitio que tu marca estaba esperando.",
"services": "Estrategia / Diseño / Desarrollo"
}
}
}

48
messages/fr.json Normal file
View File

@@ -0,0 +1,48 @@
{
"Meta": {
"title": "Kairas | Studio de design web",
"description": "Kairas est une agence de design web qui conçoit des sites raffinés, des systèmes de marque et des produits numériques."
},
"Home": {
"nav": {
"studio": "Studio",
"services": "Services",
"work": "Projets",
"notes": "Notes"
},
"localeSwitcher": {
"label": "Langue"
},
"hero": {
"kicker": "Agence de design web",
"words": ["Sites web", "à la force", "discrète."],
"interfaceLabel": "Interface sélectionnée",
"brandLabel": "Système de marque",
"interfaceCopy": "Des mises en page réglées pour une lecture calme et une action décisive.",
"ticker": ["Stratégie", "Design", "Construction"],
"copy": "Kairas conçoit et développe des sites raffinés pour fondateurs, studios et marques de services qui accordent de l'importance au goût, à la clarté et à la performance commerciale."
},
"marquee": ["Stratégie", "Design", "Développement", "Systèmes", "Lancement"],
"studio": {
"eyebrow": "À propos",
"title": "Nous façonnons des lieux numériques pour les marques qui doivent paraître exactes, intentionnelles et dignes de confiance.",
"copy": "Notre travail se situe entre studio de design et savoir-faire front-end. Nous utilisons une typographie forte, un rythme ample et des systèmes disciplinés pour composer chaque page sans la figer."
},
"servicesTitle": "Services",
"work": {
"eyebrow": "Projets sélectionnés",
"title": "Des espaces où la marque peut respirer."
},
"process": {
"title": "Processus"
},
"notes": {
"title": "Notes",
"eyebrow": "Journal du studio"
},
"footer": {
"headline": "Construisez le site que votre marque attendait.",
"services": "Stratégie / Design / Développement"
}
}
}

48
messages/hi.json Normal file
View File

@@ -0,0 +1,48 @@
{
"Meta": {
"title": "Kairas | वेब डिजाइन स्टूडियो",
"description": "Kairas परिष्कृत वेबसाइट, ब्रांड सिस्टम और डिजिटल उत्पाद बनाने वाली वेब डिजाइन फर्म है।"
},
"Home": {
"nav": {
"studio": "स्टूडियो",
"services": "सेवाएं",
"work": "काम",
"notes": "नोट्स"
},
"localeSwitcher": {
"label": "भाषा"
},
"hero": {
"kicker": "वेब डिजाइन फर्म",
"words": ["शांत", "ताकत वाली", "वेबसाइटें।"],
"interfaceLabel": "चुना हुआ इंटरफेस",
"brandLabel": "ब्रांड सिस्टम",
"interfaceCopy": "शांत पढ़ने और निर्णायक कार्रवाई के लिए बनाए गए लेआउट।",
"ticker": ["रणनीति", "डिजाइन", "निर्माण"],
"copy": "Kairas संस्थापकों, स्टूडियो और सेवा ब्रांडों के लिए परिष्कृत वेबसाइट डिजाइन और बनाता है, जहां स्वाद, स्पष्टता और व्यावसायिक प्रदर्शन मायने रखते हैं।"
},
"marquee": ["रणनीति", "डिजाइन", "विकास", "सिस्टम", "लॉन्च"],
"studio": {
"eyebrow": "हमारे बारे में",
"title": "हम उन ब्रांडों के लिए डिजिटल घर बनाते हैं जिन्हें सटीक, उद्देश्यपूर्ण और भरोसेमंद महसूस होना चाहिए।",
"copy": "हमारा काम डिजाइन स्टूडियो और फ्रंट-एंड शिल्प के बीच बैठता है। मजबूत टाइपोग्राफी, उदार गति और अनुशासित सिस्टम हर पेज को संयमित बनाते हैं।"
},
"servicesTitle": "सेवाएं",
"work": {
"eyebrow": "चुना हुआ काम",
"title": "ऐसी जगहें जहां ब्रांड सांस ले सके।"
},
"process": {
"title": "प्रक्रिया"
},
"notes": {
"title": "नोट्स",
"eyebrow": "स्टूडियो जर्नल"
},
"footer": {
"headline": "वह साइट बनाइए जिसका आपका ब्रांड इंतजार कर रहा था।",
"services": "रणनीति / डिजाइन / विकास"
}
}
}

48
messages/pt.json Normal file
View File

@@ -0,0 +1,48 @@
{
"Meta": {
"title": "Kairas | Estúdio de design web",
"description": "Kairas é uma firma de design web que cria sites refinados, sistemas de marca e produtos digitais."
},
"Home": {
"nav": {
"studio": "Estúdio",
"services": "Serviços",
"work": "Trabalho",
"notes": "Notas"
},
"localeSwitcher": {
"label": "Idioma"
},
"hero": {
"kicker": "Firma de design web",
"words": ["Sites", "com força", "silenciosa."],
"interfaceLabel": "Interface selecionada",
"brandLabel": "Sistema de marca",
"interfaceCopy": "Layouts ajustados para leitura calma e ação decisiva.",
"ticker": ["Estratégia", "Design", "Construção"],
"copy": "Kairas projeta e constrói sites refinados para fundadores, estúdios e marcas de serviço que valorizam gosto, clareza e desempenho comercial."
},
"marquee": ["Estratégia", "Design", "Desenvolvimento", "Sistemas", "Lançamento"],
"studio": {
"eyebrow": "Sobre nós",
"title": "Criamos casas digitais para marcas que precisam parecer exatas, intencionais e fáceis de confiar.",
"copy": "Nosso trabalho fica entre estúdio de design e ofício front-end. Usamos tipografia forte, ritmo generoso e sistemas disciplinados para que cada página pareça composta sem ficar estática."
},
"servicesTitle": "Serviços",
"work": {
"eyebrow": "Trabalho selecionado",
"title": "Lugares onde a marca pode respirar."
},
"process": {
"title": "Processo"
},
"notes": {
"title": "Notas",
"eyebrow": "Diário do estúdio"
},
"footer": {
"headline": "Construa o site que sua marca estava esperando.",
"services": "Estratégia / Design / Desenvolvimento"
}
}
}

48
messages/ru.json Normal file
View File

@@ -0,0 +1,48 @@
{
"Meta": {
"title": "Kairas | Студия веб-дизайна",
"description": "Kairas — студия веб-дизайна, создающая продуманные сайты, бренд-системы и цифровые продукты."
},
"Home": {
"nav": {
"studio": "Студия",
"services": "Услуги",
"work": "Работы",
"notes": "Заметки"
},
"localeSwitcher": {
"label": "Язык"
},
"hero": {
"kicker": "Студия веб-дизайна",
"words": ["Сайты", "с тихой", "силой."],
"interfaceLabel": "Выбранный интерфейс",
"brandLabel": "Бренд-система",
"interfaceCopy": "Макеты, настроенные для спокойного чтения и решительного действия.",
"ticker": ["Стратегия", "Дизайн", "Сборка"],
"copy": "Kairas проектирует и создает утонченные сайты для основателей, студий и сервисных брендов, которым важны вкус, ясность и коммерческий результат."
},
"marquee": ["Стратегия", "Дизайн", "Разработка", "Системы", "Запуск"],
"studio": {
"eyebrow": "О нас",
"title": "Мы создаем цифровые дома для брендов, которым нужны точность, намеренность и доверие.",
"copy": "Наша работа находится между дизайн-студией и фронтенд-ремеслом. Сильная типографика, щедрый ритм и дисциплинированные системы делают каждую страницу собранной, но не статичной."
},
"servicesTitle": "Услуги",
"work": {
"eyebrow": "Избранные работы",
"title": "Места, где бренд может дышать."
},
"process": {
"title": "Процесс"
},
"notes": {
"title": "Заметки",
"eyebrow": "Журнал студии"
},
"footer": {
"headline": "Создайте сайт, которого ждал ваш бренд.",
"services": "Стратегия / Дизайн / Разработка"
}
}
}

48
messages/ur.json Normal file
View File

@@ -0,0 +1,48 @@
{
"Meta": {
"title": "Kairas | ویب ڈیزائن اسٹوڈیو",
"description": "Kairas ایک ویب ڈیزائن فرم ہے جو نفیس ویب سائٹس، برانڈ سسٹمز اور ڈیجیٹل مصنوعات بناتی ہے۔"
},
"Home": {
"nav": {
"studio": "اسٹوڈیو",
"services": "خدمات",
"work": "کام",
"notes": "نوٹس"
},
"localeSwitcher": {
"label": "زبان"
},
"hero": {
"kicker": "ویب ڈیزائن فرم",
"words": ["خاموش", "قوت والی", "ویب سائٹس۔"],
"interfaceLabel": "منتخب انٹرفیس",
"brandLabel": "برانڈ سسٹم",
"interfaceCopy": "پرسکون پڑھنے اور فیصلہ کن عمل کے لیے ترتیب دیے گئے لے آؤٹس۔",
"ticker": ["حکمت عملی", "ڈیزائن", "تعمیر"],
"copy": "Kairas بانیوں، اسٹوڈیوز اور سروس برانڈز کے لیے نفیس ویب سائٹس ڈیزائن اور بناتا ہے، جہاں ذوق، وضاحت اور کاروباری کارکردگی اہم ہیں۔"
},
"marquee": ["حکمت عملی", "ڈیزائن", "ڈیولپمنٹ", "سسٹمز", "لانچ"],
"studio": {
"eyebrow": "ہمارے بارے میں",
"title": "ہم ایسے برانڈز کے لیے ڈیجیٹل گھر بناتے ہیں جنہیں درست، بامقصد اور قابل اعتماد محسوس ہونا چاہیے۔",
"copy": "ہمارا کام ڈیزائن اسٹوڈیو اور فرنٹ اینڈ ہنر کے درمیان ہے۔ مضبوط ٹائپوگرافی، کشادہ رفتار اور منظم سسٹمز ہر صفحے کو مرتب رکھتے ہیں۔"
},
"servicesTitle": "خدمات",
"work": {
"eyebrow": "منتخب کام",
"title": "وہ جگہیں جہاں برانڈ سانس لے سکے۔"
},
"process": {
"title": "عمل"
},
"notes": {
"title": "نوٹس",
"eyebrow": "اسٹوڈیو جرنل"
},
"footer": {
"headline": "وہ سائٹ بنائیں جس کا آپ کا برانڈ انتظار کر رہا تھا۔",
"services": "حکمت عملی / ڈیزائن / ڈیولپمنٹ"
}
}
}

48
messages/zh.json Normal file
View File

@@ -0,0 +1,48 @@
{
"Meta": {
"title": "Kairas | 网页设计工作室",
"description": "Kairas 为品牌打造精致的网站、品牌系统和数字产品。"
},
"Home": {
"nav": {
"studio": "工作室",
"services": "服务",
"work": "作品",
"notes": "札记"
},
"localeSwitcher": {
"label": "语言"
},
"hero": {
"kicker": "网页设计公司",
"words": ["安静而", "有力量的", "网站。"],
"interfaceLabel": "精选界面",
"brandLabel": "品牌系统",
"interfaceCopy": "为从容阅读和果断行动而调校的布局。",
"ticker": ["策略", "设计", "构建"],
"copy": "Kairas 为创始人、工作室和服务型品牌设计并构建精致的网站,兼顾品味、清晰度与商业表现。"
},
"marquee": ["策略", "设计", "开发", "系统", "发布"],
"studio": {
"eyebrow": "关于我们",
"title": "我们为需要精准、用心且值得信任的品牌塑造数字空间。",
"copy": "我们的工作介于设计工作室与前端工艺之间。我们用强有力的字体、宽裕的节奏和严谨的系统,让每个页面都保持从容而不静止。"
},
"servicesTitle": "服务",
"work": {
"eyebrow": "精选作品",
"title": "让品牌能够呼吸的地方。"
},
"process": {
"title": "流程"
},
"notes": {
"title": "札记",
"eyebrow": "工作室日志"
},
"footer": {
"headline": "构建你的品牌一直在等待的网站。",
"services": "策略 / 设计 / 开发"
}
}
}

View File

@@ -1,9 +1,24 @@
import createMiddleware from "next-intl/middleware"; import createMiddleware from "next-intl/middleware";
import {NextResponse, type NextRequest} from "next/server";
import {routing} from "./src/i18n/routing"; import {routing} from "./src/i18n/routing";
export default createMiddleware(routing); const handleI18nRouting = createMiddleware(routing);
export default function middleware(request: NextRequest) {
const legacyEnglishMarkets = ["/en-ie", "/en-gb", "/en-us"];
const pathname = request.nextUrl.pathname;
for (const legacyPath of legacyEnglishMarkets) {
if (pathname === legacyPath || pathname.startsWith(`${legacyPath}/`)) {
const url = request.nextUrl.clone();
url.pathname = pathname.replace(legacyPath, "/en");
return NextResponse.redirect(url);
}
}
return handleI18nRouting(request);
}
export const config = { export const config = {
matcher: ["/((?!api|_next|_vercel|.*\\..*).*)"], matcher: ["/((?!api|_next|_vercel|.*\\..*).*)"],
}; };

View File

@@ -2,7 +2,7 @@ import type {Metadata} from "next";
import {NextIntlClientProvider, hasLocale} from "next-intl"; import {NextIntlClientProvider, hasLocale} from "next-intl";
import {notFound} from "next/navigation"; import {notFound} from "next/navigation";
import "../globals.css"; import "../globals.css";
import {routing, type Locale} from "@/i18n/routing"; import {localeDirections, routing, type Locale} from "@/i18n/routing";
type LocaleLayoutProps = Readonly<{ type LocaleLayoutProps = Readonly<{
children: React.ReactNode; children: React.ReactNode;
@@ -70,11 +70,10 @@ export default async function LocaleLayout({
} }
return ( return (
<html lang={locale.split("-")[0]}> <html lang={locale} dir={localeDirections[locale]}>
<body> <body>
<NextIntlClientProvider>{children}</NextIntlClientProvider> <NextIntlClientProvider>{children}</NextIntlClientProvider>
</body> </body>
</html> </html>
); );
} }

View File

@@ -3,8 +3,8 @@
import {useEffect, useRef} from "react"; import {useEffect, useRef} from "react";
import {animate, createTimeline, stagger} from "animejs"; import {animate, createTimeline, stagger} from "animejs";
import {useLocale, useTranslations} from "next-intl"; import {useLocale, useTranslations} from "next-intl";
import {Link} from "@/i18n/navigation"; import {Link, useRouter} from "@/i18n/navigation";
import {routing, type Locale} from "@/i18n/routing"; import {localeNames, routing, type Locale} from "@/i18n/routing";
type Service = { type Service = {
number: string; number: string;
@@ -23,6 +23,7 @@ type ProcessStep = {
export default function Home() { export default function Home() {
const rootRef = useRef<HTMLElement>(null); const rootRef = useRef<HTMLElement>(null);
const locale = useLocale() as Locale; const locale = useLocale() as Locale;
const router = useRouter();
const t = useTranslations("Home"); const t = useTranslations("Home");
const heroWords = t.raw("hero.words") as string[]; const heroWords = t.raw("hero.words") as string[];
@@ -235,25 +236,25 @@ export default function Home() {
{t("nav.notes")} {t("nav.notes")}
</a> </a>
</div> </div>
<div className="nav-item hidden items-center gap-2 lg:flex"> <label className="nav-item hidden items-center gap-3 lg:flex">
<span className="sr-only">{t("localeSwitcher.label")}</span> <span className="text-[var(--ink-muted)]">
{routing.locales.map((option) => ( {t("localeSwitcher.label")}
<Link </span>
key={option} <select
href="/" value={locale}
locale={option} aria-label={t("localeSwitcher.label")}
aria-current={option === locale ? "page" : undefined} onChange={(event) => {
style={option === locale ? {color: "var(--paper)"} : undefined} router.replace("/", {locale: event.target.value as Locale});
className={`border px-3 py-2 transition ${ }}
option === locale className="min-w-40 border border-[var(--line)] bg-transparent px-3 py-2 text-[var(--navy)] outline-none transition hover:border-[var(--navy)] focus:border-[var(--navy)]"
? "border-[var(--navy)] bg-[var(--navy)] text-[var(--paper)]"
: "border-[var(--line)] hover:border-[var(--navy)]"
}`}
> >
{option.split("-")[1].toUpperCase()} {routing.locales.map((option) => (
</Link> <option key={option} value={option}>
{localeNames[option]}
</option>
))} ))}
</div> </select>
</label>
<a <a
href="mailto:hello@kairas.io" href="mailto:hello@kairas.io"
className="nav-item border border-[var(--navy)] px-4 py-3 transition hover:bg-[var(--navy)] hover:text-[var(--paper)]" className="nav-item border border-[var(--navy)] px-4 py-3 transition hover:bg-[var(--navy)] hover:text-[var(--paper)]"
@@ -261,27 +262,25 @@ export default function Home() {
{t("nav.contact")} {t("nav.contact")}
</a> </a>
</nav> </nav>
<div className="flex items-center justify-center gap-2 border-t border-[var(--line)] px-5 py-2 text-[11px] uppercase leading-none tracking-[0.12em] lg:hidden"> <label className="flex items-center justify-center gap-3 border-t border-[var(--line)] px-5 py-2 text-[11px] uppercase leading-none tracking-[0.12em] lg:hidden">
<span className="text-[var(--ink-muted)]"> <span className="text-[var(--ink-muted)]">
{t("localeSwitcher.label")} {t("localeSwitcher.label")}
</span> </span>
{routing.locales.map((option) => ( <select
<Link value={locale}
key={option} aria-label={t("localeSwitcher.label")}
href="/" onChange={(event) => {
locale={option} router.replace("/", {locale: event.target.value as Locale});
aria-current={option === locale ? "page" : undefined} }}
style={option === locale ? {color: "var(--paper)"} : undefined} className="min-w-36 border border-[var(--line)] bg-transparent px-3 py-2 text-[var(--navy)] outline-none"
className={`border px-3 py-2 transition ${
option === locale
? "border-[var(--navy)] bg-[var(--navy)] text-[var(--paper)]"
: "border-[var(--line)] hover:border-[var(--navy)]"
}`}
> >
{option.split("-")[1].toUpperCase()} {routing.locales.map((option) => (
</Link> <option key={option} value={option}>
{localeNames[option]}
</option>
))} ))}
</div> </select>
</label>
</header> </header>
<section className="relative z-10 mx-auto grid min-h-screen max-w-[1500px] content-end gap-12 px-5 pb-12 pt-40 sm:px-8 md:pt-36 lg:grid-cols-[1.05fr_0.95fr] lg:px-12"> <section className="relative z-10 mx-auto grid min-h-screen max-w-[1500px] content-end gap-12 px-5 pb-12 pt-40 sm:px-8 md:pt-36 lg:grid-cols-[1.05fr_0.95fr] lg:px-12">

View File

@@ -2,15 +2,43 @@ import {getRequestConfig} from "next-intl/server";
import {hasLocale} from "next-intl"; import {hasLocale} from "next-intl";
import {routing} from "./routing"; import {routing} from "./routing";
type Messages = Record<string, unknown>;
function mergeMessages(base: Messages, override: Messages): Messages {
const merged: Messages = {...base};
for (const [key, value] of Object.entries(override)) {
const baseValue = merged[key];
if (
value &&
baseValue &&
typeof value === "object" &&
typeof baseValue === "object" &&
!Array.isArray(value) &&
!Array.isArray(baseValue)
) {
merged[key] = mergeMessages(baseValue as Messages, value as Messages);
} else {
merged[key] = value;
}
}
return merged;
}
export default getRequestConfig(async ({requestLocale}) => { export default getRequestConfig(async ({requestLocale}) => {
const requested = await requestLocale; const requested = await requestLocale;
const locale = hasLocale(routing.locales, requested) const locale = hasLocale(routing.locales, requested)
? requested ? requested
: routing.defaultLocale; : routing.defaultLocale;
const defaultMessages = (await import("../../messages/en.json")).default;
const localeMessages =
locale === routing.defaultLocale
? defaultMessages
: (await import(`../../messages/${locale}.json`)).default;
return { return {
locale, locale,
messages: (await import(`../../messages/${locale}.json`)).default, messages: mergeMessages(defaultMessages, localeMessages),
}; };
}); });

View File

@@ -1,11 +1,36 @@
import {defineRouting} from "next-intl/routing"; import {defineRouting} from "next-intl/routing";
export const routing = defineRouting({ export const routing = defineRouting({
locales: ["en-ie", "en-gb", "en-us"], locales: ["en", "zh", "hi", "es", "fr", "ar", "bn", "pt", "ru", "ur"],
defaultLocale: "en-ie", defaultLocale: "en",
localePrefix: "always", localePrefix: "always",
localeDetection: true, localeDetection: true,
}); });
export type Locale = (typeof routing.locales)[number]; export type Locale = (typeof routing.locales)[number];
export const localeNames: Record<Locale, string> = {
en: "English",
zh: "中文",
hi: "हिन्दी",
es: "Español",
fr: "Français",
ar: "العربية",
bn: "বাংলা",
pt: "Português",
ru: "Русский",
ur: "اردو",
};
export const localeDirections: Record<Locale, "ltr" | "rtl"> = {
en: "ltr",
zh: "ltr",
hi: "ltr",
es: "ltr",
fr: "ltr",
ar: "rtl",
bn: "ltr",
pt: "ltr",
ru: "ltr",
ur: "rtl",
};