Skip to content

Commit

Permalink
Merge pull request #5 from unchain-tech/feat/bilingual
Browse files Browse the repository at this point in the history
二言語対応(日英)
  • Loading branch information
neila authored Sep 18, 2024
2 parents ed1b334 + 518f196 commit c83f2d8
Show file tree
Hide file tree
Showing 30 changed files with 528 additions and 87 deletions.
11 changes: 11 additions & 0 deletions i18n.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { I18nConfig } from 'next-translate';

export const i18nConfig = {
locales: ['en', 'ja'],
defaultLocale: 'ja',
loader: false,
pages: {
'*': ['common'],
},
defaultNS: 'common',
} satisfies I18nConfig;
29 changes: 29 additions & 0 deletions locales/en/common.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"LOCALESWITCH": "ja",
"MENU": { "LANGUAGE": "日本語" },
"TOP": {
"INTRO": "UNCHAIN is a grassroots community of web3 engineers, turning to their ideas into reality."
},
"PROJECTS": {
"1": "Hands-on tutorials to help you understand how to build dApps on public blockchains.",
"2": "Completely free and open-source."
},
"VALUES": {
"1": {
"HEADING": "Project-Based Learning",
"DESCRIPTION": "Gain practical development skills in smart contracts and web applications through a wide range of hands-on, project based tutorials."
},
"2": {
"HEADING": "Community Growth",
"DESCRIPTION": "Find other people with similar passions, and share ideas with each other for mutual growth."
},
"3": {
"HEADING": "Open-Source",
"DESCRIPTION": "Anyone can see and verify our projects, and even contribute to it themselves. Everything is provided free and to the public."
}
},
"CONTACT": {
"TITLE": "CONTACT FORM"
},
"PRIVACYPOLICY": "Privacy Policy"
}
29 changes: 29 additions & 0 deletions locales/ja/common.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"LOCALESWITCH": "en",
"MENU": { "LANGUAGE": "English" },
"TOP": {
"INTRO": "UNCHAINは、プロジェクト開発を通して技術を学び、実践経験を積むことで自分のアイデアを形にする力を身につける、エンジニアのための有志コミュニティです。"
},
"PROJECTS": {
"1": "public chain上でdApp開発を実践できる、25の開発プロジェクトをオープンソースで提供",
"2": "完全無料・オープンソース"
},
"VALUES": {
"1": {
"HEADING": "プロジェクト型の学習",
"DESCRIPTION": "プロジェクト型の学習コンテンツを通して、スマートコントラクトやweb開発の実践的なスキルを身につけることができます。"
},
"2": {
"HEADING": "共に学ぶコミュニティ",
"DESCRIPTION": "同じ興味や志を持つ仲間を見つけ、互いの知見を共有し合うことで、切磋琢磨することができます。"
},
"3": {
"HEADING": "オープンソース",
"DESCRIPTION": "誰でもソースコードを検証し、必要とあれば追記編集することができます。コンテンツは全て無料で公開されています。"
}
},
"CONTACT": {
"TITLE": "問い合わせフォーム"
},
"PRIVACYPOLICY": "プライバシーポリシー"
}
1 change: 0 additions & 1 deletion next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ const nextConfig = {
reactStrictMode: true,
output: 'export',
swcMinify: true,
// i18n: { locales: ['en', 'ja'], defaultLocale: 'en', localeDetection: false },
images: {
unoptimized: true,
remotePatterns: [
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
"airtable": "^0.12.2",
"lru-cache": "^10.2.0",
"next": "^14.1.4",
"next-language-detector": "^1.1.0",
"next-translate": "^2.6.2",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
Expand Down
File renamed without changes.
35 changes: 35 additions & 0 deletions src/components/_shared/Link/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import NextLink from 'next/link';
import { useRouter } from 'next/router';
import type { ReactNode } from 'react';

interface LinkProps {
children: ReactNode;
skipLocaleHandling?: boolean;
locale?: string;
href: string;
target?: string;
}

export const Link = ({
children,
skipLocaleHandling,
target,
...rest
}: LinkProps) => {
const router = useRouter();
const locale = rest.locale || (router.query.locale as string) || '';

let href = rest.href || router.asPath;
if (href.indexOf('http') === 0) skipLocaleHandling = true;
if (locale && !skipLocaleHandling) {
href = href
? `/${locale}${href}`
: router.pathname.replace('[locale]', locale);
}

return (
<NextLink href={href} target={target}>
{children}
</NextLink>
);
};
4 changes: 3 additions & 1 deletion src/components/layouts/baseLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ export const Layout: FC<Props> = ({ pageTitle, children }) => {

<main>
<Header />
<div id="body">{children}</div>
<div id="body" className="pt-24">
{children}
</div>
<Footer />
</main>
</>
Expand Down
44 changes: 44 additions & 0 deletions src/components/molecules/LanguageSwitcher/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import Link from 'next/link';
import { useRouter } from 'next/router';

import { languageDetector } from '@/lib/languageDetector';

interface LanguageSwitcherProps {
locale: string;
href?: string;
asPath?: string;
}

export const LanguageSwitcher = ({
locale,
...rest
}: LanguageSwitcherProps) => {
const router = useRouter();

let href = rest.href || router.asPath;
let pName = router.pathname;
// biome-ignore lint/complexity/noForEach: readability
Object.keys(router.query).forEach((k) => {
if (k === 'locale') {
pName = pName.replace(`[${k}]`, locale);
return;
}
pName = pName.replace(`[${k}]`, String(router.query[k]));
});
if (locale) {
href = rest.href ? `/${locale}${rest.href}` : pName;
}

return (
<Link
href={href}
onClick={() =>
languageDetector.cache ? languageDetector.cache(locale) : {}
}
>
<button type="button">
<p className="text-xl">{locale}</p>
</button>
</Link>
);
};
22 changes: 20 additions & 2 deletions src/components/molecules/contactform/static.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import { useRouter } from 'next/router';
import { FC, useState } from 'react';
import { Controller, SubmitHandler, useForm } from 'react-hook-form';

import { useI18n } from '@/hooks/useI18n';
import { languageDetector } from '@/lib/languageDetector';
import { type FormInput } from '@/types/form';
import {
table_websiteInquiry,
Expand All @@ -22,6 +24,9 @@ import {

const ContactFormDangerous: FC = () => {
const router = useRouter();
const { t } = useI18n();
const detectedLng = languageDetector.detect();

const {
register,
handleSubmit,
Expand Down Expand Up @@ -276,15 +281,28 @@ const ContactFormDangerous: FC = () => {
label={
<div className="flex flex-row space-x-1">
<p>
{detectedLng == 'en' ? (
<span className="hover:no-cursor text-sm">
Agree to the{' '}
</span>
) : (
<></>
)}

<a
href="https://example.com"
className="text-sm font-light underline"
target="_blank"
rel="noreferrer"
>
プライバシーポリシー
{t('PRIVACY POLICY')}
</a>
<span className="hover:no-cursor text-sm">に同意</span>

{detectedLng == 'ja' ? (
<span className="hover:no-cursor text-sm">に同意</span>
) : (
<></>
)}
</p>
</div>
}
Expand Down
16 changes: 13 additions & 3 deletions src/components/molecules/header/index.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,34 @@
import Image from 'next/image';
import Link from 'next/link';
import { FC } from 'react';

import { LanguageSwitcher } from '../LanguageSwitcher';
import { NavMenu } from './navmenu';
import UNCHAIN_logo from '/public/logo/UNCHAIN_logo_ondark.png';

import { useRouteRedirect } from '@/hooks/useRouteRedirect';

export const Header: FC = () => {
const { redirect } = useRouteRedirect();

return (
<header className="header desktop:px-8">
{/*Logo*/}
<Link href="/" passHref={true}>
<button type="button" onClick={() => redirect('/')}>
<Image
className="cursor-pointer rounded-full"
alt="UNCHAIN logo"
src={UNCHAIN_logo}
width={240}
height={100}
/>
</Link>
</button>
<NavMenu />
{/* Language */}
<div className="flex items-center px-2 space-x-2">
<LanguageSwitcher locale="en" />
<span>/</span>
<LanguageSwitcher locale="ja" />
</div>
</header>
);
};
Expand Down
10 changes: 0 additions & 10 deletions src/components/molecules/header/navmenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,6 @@ export const NavMenu: FC = () => {
<Item id="start" href="https://discord.gg/w3AyyvKypT/" text="JOIN" />
<Item id="start" href="https://buidl.unchain.tech/" text="BUIDL" />
<Item id="contact" href="/contact" text="CONTACT" />

{/* language switch */}
{/* <Link
className="header-menuitem"
href={t.LOCALESWITCH + page.pathname}
locale={t.LOCALESWITCH}
passHref={true}
>
{t.MENU.LANGUAGE}
</Link> */}
</div>
);
};
Expand Down
5 changes: 4 additions & 1 deletion src/components/sections/firstview/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ import { FC } from 'react';

import SupportedNetworks from './supportednetworks';

import { useI18n } from '@/hooks/useI18n';

export const FirstView: FC = () => {
const { t } = useI18n();
return (
<div
id="firstview"
Expand All @@ -22,7 +25,7 @@ export const FirstView: FC = () => {
id="blurb"
className="w-5/6 py-2 text-base laptop:w-1/2 desktop:py-4 desktop:text-lg"
>
UNCHAINは、プロジェクト開発を通して技術を学び、実践経験を積むことで自分のアイデアを形にする力を身につける、エンジニアのための有志コミュニティです。
{t('TOP.INTRO')}
</p>

<Link id="start_building" href="https://buidl.unchain.tech/" passHref>
Expand Down
7 changes: 4 additions & 3 deletions src/components/sections/projects/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import lessoncards_wide from 'public/projects_wide.png';
import lessoncards_widest from 'public/projects_widest.png';
import { FC } from 'react';

import { useI18n } from '@/hooks/useI18n';
import { useWindowDimensions } from '@/hooks/useWindowDimensions';

export const Projects: FC = () => {
const { width } = useWindowDimensions();
const { t } = useI18n();

const BannerImage = () => {
if (width! < 1025) {
Expand Down Expand Up @@ -42,11 +44,10 @@ export const Projects: FC = () => {

<div id="items" className="w-3/4 space-y-2 pt-4 desktop:pt-8">
<p className="text-xs leading-relaxed desktop:text-lg">
✅ public
chain上でdApp開発を実践できる、25の開発プロジェクトをオープンソースで提供
{t('PROJECTS.1')}
</p>
<p className="text-xs leading-relaxed desktop:text-lg">
ブロックチェーンアプリの開発歴をNFTで証明
{t('PROJECTS.1')}
</p>
</div>
</div>
Expand Down
15 changes: 9 additions & 6 deletions src/components/sections/services/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import Image from 'next/image';
import { FC } from 'react';

import { useI18n } from '@/hooks/useI18n';

export const Services: FC = () => {
const { t } = useI18n();
const Card = ({
iconType,
heading,
Expand Down Expand Up @@ -44,18 +47,18 @@ export const Services: FC = () => {
>
<Card
iconType="1"
heading="プロジェクト型の学習"
description="プロジェクト型の学習コンテンツを通して、スマートコントラクトやweb開発の実践的なスキルを身につけることができます。"
heading={t('VALUES.1.HEADING')}
description={t('VALUES.1.DESCRIPTION')}
/>
<Card
iconType="2"
heading="共に学ぶコミュニティ"
description="同じ興味や志を持つ仲間を見つけ、互いの知見を共有し合うことで、切磋琢磨することができます。"
heading={t('VALUES.2.HEADING')}
description={t('VALUES.2.DESCRIPTION')}
/>
<Card
iconType="3"
heading="NFTで学習歴を証明"
description="各メンバーのコミュニティでの活動や貢献の証を、トークンを用いてブロックチェーン上に発行しています。"
heading={t('VALUES.3.HEADING')}
description={t('VALUES.3.DESCRIPTION')}
/>
</div>
</div>
Expand Down
12 changes: 12 additions & 0 deletions src/hooks/useI18n.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import useTranslation from 'next-translate/useTranslation';

import { i18nConfig } from '../../i18n';

interface IUseI18n {
namespace?: string;
}

export const useI18n = ({ namespace }: IUseI18n = {}) => {
const { t } = useTranslation(namespace ? namespace : i18nConfig.defaultNS);
return { t };
};
10 changes: 0 additions & 10 deletions src/hooks/useLocale.ts

This file was deleted.

Loading

0 comments on commit c83f2d8

Please sign in to comment.