Add localization for header menus/some modals
This commit is contained in:
parent
5ea57f0e07
commit
b91d84028f
7 changed files with 170 additions and 26 deletions
|
|
@ -1,21 +1,24 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import { useTranslation } from 'next-i18next'
|
||||||
import * as Dialog from '@radix-ui/react-dialog'
|
import * as Dialog from '@radix-ui/react-dialog'
|
||||||
|
|
||||||
import CrossIcon from '~public/icons/Cross.svg'
|
import CrossIcon from '~public/icons/Cross.svg'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
|
||||||
const AboutModal = () => {
|
const AboutModal = () => {
|
||||||
|
const { t } = useTranslation('common')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog.Root>
|
<Dialog.Root>
|
||||||
<Dialog.Trigger asChild>
|
<Dialog.Trigger asChild>
|
||||||
<li className="MenuItem">
|
<li className="MenuItem">
|
||||||
<span>About</span>
|
<span>{t('modals.about.title')}</span>
|
||||||
</li>
|
</li>
|
||||||
</Dialog.Trigger>
|
</Dialog.Trigger>
|
||||||
<Dialog.Portal>
|
<Dialog.Portal>
|
||||||
<Dialog.Content className="About Dialog" onOpenAutoFocus={ (event) => event.preventDefault() }>
|
<Dialog.Content className="About Dialog" onOpenAutoFocus={ (event) => event.preventDefault() }>
|
||||||
<div className="DialogHeader">
|
<div className="DialogHeader">
|
||||||
<Dialog.Title className="DialogTitle">About</Dialog.Title>
|
<Dialog.Title className="DialogTitle">{t('menu.about')}</Dialog.Title>
|
||||||
<Dialog.Close className="DialogClose" asChild>
|
<Dialog.Close className="DialogClose" asChild>
|
||||||
<span>
|
<span>
|
||||||
<CrossIcon />
|
<CrossIcon />
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,10 @@
|
||||||
font-size: $font-small;
|
font-size: $font-small;
|
||||||
line-height: 1.1;
|
line-height: 1.1;
|
||||||
max-width: 300px;
|
max-width: 300px;
|
||||||
|
|
||||||
|
&.jp {
|
||||||
|
max-width: 270px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { useCookies } from 'react-cookie'
|
import { useCookies } from 'react-cookie'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
import { useSnapshot } from 'valtio'
|
import { useSnapshot } from 'valtio'
|
||||||
|
import { useTranslation } from 'next-i18next'
|
||||||
|
|
||||||
import * as Dialog from '@radix-ui/react-dialog'
|
import * as Dialog from '@radix-ui/react-dialog'
|
||||||
import * as Switch from '@radix-ui/react-switch'
|
import * as Switch from '@radix-ui/react-switch'
|
||||||
|
|
@ -15,8 +17,13 @@ import CrossIcon from '~public/icons/Cross.svg'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
|
||||||
const AccountModal = () => {
|
const AccountModal = () => {
|
||||||
|
const { t } = useTranslation('common')
|
||||||
|
|
||||||
const { account } = useSnapshot(accountState)
|
const { account } = useSnapshot(accountState)
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const locale = (router.locale && ['en', 'ja'].includes(router.locale)) ? router.locale : 'en'
|
||||||
|
|
||||||
// Cookies
|
// Cookies
|
||||||
const [accountCookies] = useCookies(['account'])
|
const [accountCookies] = useCookies(['account'])
|
||||||
const [userCookies, setUserCookies] = useCookies(['user'])
|
const [userCookies, setUserCookies] = useCookies(['user'])
|
||||||
|
|
@ -46,7 +53,7 @@ const AccountModal = () => {
|
||||||
const pictureOptions = (
|
const pictureOptions = (
|
||||||
pictureData.sort((a, b) => (a.name.en > b.name.en) ? 1 : -1).map((item, i) => {
|
pictureData.sort((a, b) => (a.name.en > b.name.en) ? 1 : -1).map((item, i) => {
|
||||||
return (
|
return (
|
||||||
<option key={`picture-${i}`} value={item.filename}>{item.name.en}</option>
|
<option key={`picture-${i}`} value={item.filename}>{item.name[locale]}</option>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
@ -109,14 +116,14 @@ const AccountModal = () => {
|
||||||
<Dialog.Root open={open} onOpenChange={openChange}>
|
<Dialog.Root open={open} onOpenChange={openChange}>
|
||||||
<Dialog.Trigger asChild>
|
<Dialog.Trigger asChild>
|
||||||
<li className="MenuItem">
|
<li className="MenuItem">
|
||||||
<span>Settings</span>
|
<span>{t('menu.settings')}</span>
|
||||||
</li>
|
</li>
|
||||||
</Dialog.Trigger>
|
</Dialog.Trigger>
|
||||||
<Dialog.Portal>
|
<Dialog.Portal>
|
||||||
<Dialog.Content className="Account Dialog" onOpenAutoFocus={ (event) => event.preventDefault() }>
|
<Dialog.Content className="Account Dialog" onOpenAutoFocus={ (event) => event.preventDefault() }>
|
||||||
<div className="DialogHeader">
|
<div className="DialogHeader">
|
||||||
<div className="DialogTop">
|
<div className="DialogTop">
|
||||||
<Dialog.Title className="SubTitle">Account Settings</Dialog.Title>
|
<Dialog.Title className="SubTitle">{t('modals.settings.title')}</Dialog.Title>
|
||||||
<Dialog.Title className="DialogTitle">@{account.user?.username}</Dialog.Title>
|
<Dialog.Title className="DialogTitle">@{account.user?.username}</Dialog.Title>
|
||||||
</div>
|
</div>
|
||||||
<Dialog.Close className="DialogClose" asChild>
|
<Dialog.Close className="DialogClose" asChild>
|
||||||
|
|
@ -129,7 +136,7 @@ const AccountModal = () => {
|
||||||
<form onSubmit={update}>
|
<form onSubmit={update}>
|
||||||
<div className="field">
|
<div className="field">
|
||||||
<div className="left">
|
<div className="left">
|
||||||
<label>Picture</label>
|
<label>{t('modals.settings.labels.picture')}</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={`preview ${pictureData.find(i => i.filename === picture)?.element}`}>
|
<div className={`preview ${pictureData.find(i => i.filename === picture)?.element}`}>
|
||||||
|
|
@ -147,18 +154,18 @@ const AccountModal = () => {
|
||||||
</div>
|
</div>
|
||||||
<div className="field">
|
<div className="field">
|
||||||
<div className="left">
|
<div className="left">
|
||||||
<label>Language</label>
|
<label>{t('modals.settings.labels.language')}</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<select name="language" onChange={handleLanguageChange} value={language} ref={languageSelect}>
|
<select name="language" onChange={handleLanguageChange} value={language} ref={languageSelect}>
|
||||||
<option key="en" value="en">English</option>
|
<option key="en" value="en">{t('modals.settings.language.english')}</option>
|
||||||
<option key="jp" value="jp">Japanese</option>
|
<option key="jp" value="jp">{t('modals.settings.language.japanese')}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div className="field">
|
<div className="field">
|
||||||
<div className="left">
|
<div className="left">
|
||||||
<label>Private</label>
|
<label>{t('modals.settings.labels.private')}</label>
|
||||||
<p>Hide your profile and prevent your grids from showing up in collections</p>
|
<p className={locale}>{t('modals.settings.descriptions.private')}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Switch.Root className="Switch" onCheckedChange={handlePrivateChange} checked={privateProfile}>
|
<Switch.Root className="Switch" onCheckedChange={handlePrivateChange} checked={privateProfile}>
|
||||||
|
|
@ -166,7 +173,7 @@ const AccountModal = () => {
|
||||||
</Switch.Root>
|
</Switch.Root>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button>Save settings</Button>
|
<Button>{t('modals.settings.buttons.confirm')}</Button>
|
||||||
</form>
|
</form>
|
||||||
</Dialog.Content>
|
</Dialog.Content>
|
||||||
<Dialog.Overlay className="Overlay" />
|
<Dialog.Overlay className="Overlay" />
|
||||||
|
|
|
||||||
|
|
@ -90,14 +90,14 @@ const BottomHeader = () => {
|
||||||
<AlertDialog.Overlay className="Overlay" />
|
<AlertDialog.Overlay className="Overlay" />
|
||||||
<AlertDialog.Content className="Dialog">
|
<AlertDialog.Content className="Dialog">
|
||||||
<AlertDialog.Title className="DialogTitle">
|
<AlertDialog.Title className="DialogTitle">
|
||||||
{t('delete_team.title')}
|
{t('modals.delete_team.title')}
|
||||||
</AlertDialog.Title>
|
</AlertDialog.Title>
|
||||||
<AlertDialog.Description className="DialogDescription">
|
<AlertDialog.Description className="DialogDescription">
|
||||||
{t('delete_team.description')}
|
{t('modals.delete_team.description')}
|
||||||
</AlertDialog.Description>
|
</AlertDialog.Description>
|
||||||
<div className="actions">
|
<div className="actions">
|
||||||
<AlertDialog.Cancel className="Button modal">{t('delete_team.buttons.cancel')}</AlertDialog.Cancel>
|
<AlertDialog.Cancel className="Button modal">{t('modals.delete_team.buttons.cancel')}</AlertDialog.Cancel>
|
||||||
<AlertDialog.Action className="Button modal destructive" onClick={(e) => deleteTeam(e)}>{t('delete_team.buttons.confirm')}</AlertDialog.Action>
|
<AlertDialog.Action className="Button modal destructive" onClick={(e) => deleteTeam(e)}>{t('modals.delete_team.buttons.confirm')}</AlertDialog.Action>
|
||||||
</div>
|
</div>
|
||||||
</AlertDialog.Content>
|
</AlertDialog.Content>
|
||||||
</AlertDialog.Portal>
|
</AlertDialog.Portal>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { useCookies } from 'react-cookie'
|
import { useCookies } from 'react-cookie'
|
||||||
|
import { useTranslation } from 'next-i18next'
|
||||||
|
|
||||||
import AboutModal from '~components/AboutModal'
|
import AboutModal from '~components/AboutModal'
|
||||||
import AccountModal from '~components/AccountModal'
|
import AccountModal from '~components/AccountModal'
|
||||||
|
|
@ -16,6 +17,8 @@ interface Props {
|
||||||
}
|
}
|
||||||
|
|
||||||
const HeaderMenu = (props: Props) => {
|
const HeaderMenu = (props: Props) => {
|
||||||
|
const { t } = useTranslation('common')
|
||||||
|
|
||||||
const [accountCookies] = useCookies(['account'])
|
const [accountCookies] = useCookies(['account'])
|
||||||
const [userCookies] = useCookies(['user'])
|
const [userCookies] = useCookies(['user'])
|
||||||
|
|
||||||
|
|
@ -35,22 +38,22 @@ const HeaderMenu = (props: Props) => {
|
||||||
/profile/${userCookies.user.picture}@2x.png 2x`}
|
/profile/${userCookies.user.picture}@2x.png 2x`}
|
||||||
src={`/profile/${userCookies.user.picture}.png`}
|
src={`/profile/${userCookies.user.picture}.png`}
|
||||||
/>
|
/>
|
||||||
</div
|
</div>
|
||||||
></Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li className="MenuItem">
|
<li className="MenuItem">
|
||||||
<Link href={`/saved` || ''}>Saved</Link>
|
<Link href={`/saved` || ''}>{t('menu.saved')}</Link>
|
||||||
</li>
|
</li>
|
||||||
</div>
|
</div>
|
||||||
<div className="MenuGroup">
|
<div className="MenuGroup">
|
||||||
<li className="MenuItem">
|
<li className="MenuItem">
|
||||||
<Link href='/teams'>Teams</Link>
|
<Link href='/teams'>{t('menu.teams')}</Link>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li className="MenuItem disabled">
|
<li className="MenuItem disabled">
|
||||||
<div>
|
<div>
|
||||||
<span>Guides</span>
|
<span>{t('menu.guides')}</span>
|
||||||
<i className="tag">Coming Soon</i>
|
<i className="tag">{t('coming_soon')}</i>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -58,7 +61,7 @@ const HeaderMenu = (props: Props) => {
|
||||||
<AboutModal />
|
<AboutModal />
|
||||||
<AccountModal />
|
<AccountModal />
|
||||||
<li className="MenuItem" onClick={props.logout}>
|
<li className="MenuItem" onClick={props.logout}>
|
||||||
<span>Logout</span>
|
<span>{t('menu.logout')}</span>
|
||||||
</li>
|
</li>
|
||||||
</div>
|
</div>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
@ -74,13 +77,13 @@ const HeaderMenu = (props: Props) => {
|
||||||
</div>
|
</div>
|
||||||
<div className="MenuGroup">
|
<div className="MenuGroup">
|
||||||
<li className="MenuItem">
|
<li className="MenuItem">
|
||||||
<Link href='/teams'>Teams</Link>
|
<Link href='/teams'>{t('menu.teams')}</Link>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li className="MenuItem disabled">
|
<li className="MenuItem disabled">
|
||||||
<div>
|
<div>
|
||||||
<span>Guides</span>
|
<span>{t('menu.guides')}</span>
|
||||||
<i className="tag">Coming Soon</i>
|
<i className="tag">{t('menu.logout')}</i>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
63
public/locales/en/common.json
Normal file
63
public/locales/en/common.json
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
{
|
||||||
|
"buttons": {
|
||||||
|
"copy": "Copy link",
|
||||||
|
"delete": "Delete team",
|
||||||
|
"edit_info": "Edit info",
|
||||||
|
"hide_info": "Hide info",
|
||||||
|
"menu": "Menu",
|
||||||
|
"new": "New"
|
||||||
|
},
|
||||||
|
"modals": {
|
||||||
|
"about": {
|
||||||
|
"title": "About"
|
||||||
|
},
|
||||||
|
"delete_team": {
|
||||||
|
"title": "Delete team",
|
||||||
|
"description": "Are you sure you want to permanently delete this team?",
|
||||||
|
"buttons": {
|
||||||
|
"confirm": "Yes, delete",
|
||||||
|
"cancel": "Nevermind"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"title": "Account Settings",
|
||||||
|
"labels": {
|
||||||
|
"picture": "Picture",
|
||||||
|
"language": "Language",
|
||||||
|
"private": "Private"
|
||||||
|
},
|
||||||
|
"descriptions": {
|
||||||
|
"private": "Hide your profile and prevent your grids from showing up in collections"
|
||||||
|
},
|
||||||
|
"language": {
|
||||||
|
"english": "English",
|
||||||
|
"japanese": "Japanese"
|
||||||
|
},
|
||||||
|
"buttons": {
|
||||||
|
"confirm": "Save settings"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"menu": {
|
||||||
|
"about": "About",
|
||||||
|
"guides": "Guides",
|
||||||
|
"saved": "Saved",
|
||||||
|
"settings": "Settings",
|
||||||
|
"teams": "Teams",
|
||||||
|
"logout": "Logout"
|
||||||
|
},
|
||||||
|
"party": {
|
||||||
|
"segmented_control": {
|
||||||
|
"class": "Class",
|
||||||
|
"characters": "Characters",
|
||||||
|
"weapons": "Weapons",
|
||||||
|
"summons": "Summons"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"teams": {
|
||||||
|
"title": "Discover Teams",
|
||||||
|
"loading": "Loading teams...",
|
||||||
|
"not_found": "No teams found"
|
||||||
|
},
|
||||||
|
"coming_soon": "Coming Soon"
|
||||||
|
}
|
||||||
64
public/locales/ja/common.json
Normal file
64
public/locales/ja/common.json
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
{
|
||||||
|
"buttons": {
|
||||||
|
"copy": "リンクをコピー",
|
||||||
|
"delete": "編成を削除",
|
||||||
|
"show_info": "詳細を編集",
|
||||||
|
"hide_info": "詳細を非表示",
|
||||||
|
"menu": "メニュー",
|
||||||
|
"new": "作成"
|
||||||
|
},
|
||||||
|
"modals": {
|
||||||
|
"about": {
|
||||||
|
"title": "このサイトについて"
|
||||||
|
},
|
||||||
|
"delete_team": {
|
||||||
|
"title": "編成を削除しますか",
|
||||||
|
"description": "編成を削除する操作は取り消せません。",
|
||||||
|
"buttons": {
|
||||||
|
"confirm": "削除",
|
||||||
|
"cancel": "キャンセル"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"title": "アカウント設定",
|
||||||
|
"labels": {
|
||||||
|
"picture": "プロフィール画像",
|
||||||
|
"language": "言語",
|
||||||
|
"private": "プライベート"
|
||||||
|
},
|
||||||
|
"descriptions": {
|
||||||
|
"private": "プロフィールを隠し、編成をコレクションに表示されないようにします"
|
||||||
|
},
|
||||||
|
"language": {
|
||||||
|
"english": "英語",
|
||||||
|
"japanese": "日本語"
|
||||||
|
},
|
||||||
|
"buttons": {
|
||||||
|
"confirm": "設定を保存する"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"menu": {
|
||||||
|
"about": "このサイトについて",
|
||||||
|
"guides": "攻略",
|
||||||
|
"saved": "保存した編成",
|
||||||
|
"settings": "アカウント設定",
|
||||||
|
"teams": "編成一覧",
|
||||||
|
"logout": "ログアウト"
|
||||||
|
},
|
||||||
|
"party": {
|
||||||
|
"segmented_control": {
|
||||||
|
"class": "ジョブ",
|
||||||
|
"characters": "キャラ",
|
||||||
|
"weapons": "武器",
|
||||||
|
"summons": "召喚石"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"teams": {
|
||||||
|
"title": "編成一覧",
|
||||||
|
"loading": "ロード中...",
|
||||||
|
"not_found": "編成は見つかりませんでした"
|
||||||
|
},
|
||||||
|
"coming_soon": "開発中"
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
Reference in a new issue