mirror of
https://github.com/uetchy/namae.git
synced 2025-10-15 07:32:19 +09:00
feat: i18n
This commit is contained in:
@@ -5,16 +5,21 @@
|
||||
"build": "react-scripts build",
|
||||
"eject": "react-scripts eject",
|
||||
"now-build": "yarn build",
|
||||
"now-dev": "BROWSER=false react-scripts start",
|
||||
"now-dev": "react-scripts start",
|
||||
"start": "react-scripts start",
|
||||
"test": "react-scripts test"
|
||||
},
|
||||
"dependencies": {
|
||||
"fetch-suspense": "^1.2.0",
|
||||
"i18next": "^17.0.8",
|
||||
"i18next-browser-languagedetector": "^3.0.1",
|
||||
"i18next-xhr-backend": "^3.0.0",
|
||||
"isomorphic-unfetch": "^3.0.0",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6",
|
||||
"react-ga": "^2.6.0",
|
||||
"react-helmet": "^5.2.1",
|
||||
"react-i18next": "^10.11.4",
|
||||
"react-icons": "^3.7.0",
|
||||
"react-scripts": "3.0.1",
|
||||
"react-spinners": "^0.5.13",
|
||||
|
18
web/public/locales/en/translation.json
Normal file
18
web/public/locales/en/translation.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"title": "name new project",
|
||||
"description": "namæ saves your time searching around registries and checking if the desired name is ready for use.",
|
||||
"placeholder": "search",
|
||||
"providers": {
|
||||
"domains": "Domains",
|
||||
"github": "Github Organization",
|
||||
"npm": "npm",
|
||||
"pypi": "PyPI",
|
||||
"rubygems": "RubyGems",
|
||||
"rust": "Rust",
|
||||
"homebrew": "Homebrew",
|
||||
"jsorg": "js.org",
|
||||
"s3": "AWS S3",
|
||||
"twitter": "Twitter",
|
||||
"slack": "Slack"
|
||||
}
|
||||
}
|
18
web/public/locales/ja/translation.json
Normal file
18
web/public/locales/ja/translation.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"title": "その名前、もう使われてる?",
|
||||
"description": "namæ をつかって、思いついた「名前」が誰にも使われていないか調べましょう。",
|
||||
"placeholder": "検索",
|
||||
"providers": {
|
||||
"domains": "ドメイン",
|
||||
"github": "Github Organization",
|
||||
"npm": "npm",
|
||||
"pypi": "PyPI",
|
||||
"rubygems": "RubyGems",
|
||||
"rust": "Rust",
|
||||
"homebrew": "Homebrew",
|
||||
"jsorg": "js.org",
|
||||
"s3": "AWS S3",
|
||||
"twitter": "Twitter",
|
||||
"slack": "Slack"
|
||||
}
|
||||
}
|
@@ -1,5 +1,7 @@
|
||||
import React, { useState, useEffect, useRef } from 'react'
|
||||
import styled, { createGlobalStyle } from 'styled-components'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import Welcome from './components/Welcome'
|
||||
import Footer from './components/Footer'
|
||||
@@ -25,6 +27,7 @@ export default function App() {
|
||||
const [query, setQuery] = useDeferredState(1000)
|
||||
const [inputValue, setInputValue] = useState('')
|
||||
const inputRef = useRef()
|
||||
const { t } = useTranslation()
|
||||
|
||||
const queryGiven = query && query.length > 0
|
||||
|
||||
@@ -44,10 +47,18 @@ export default function App() {
|
||||
return (
|
||||
<>
|
||||
<GlobalStyle />
|
||||
<Helmet>
|
||||
<title>namaæ — {t('title')}</title>
|
||||
</Helmet>
|
||||
<Header>
|
||||
<InputContainer>
|
||||
<Logo onClick={onLogoClick}>namæ</Logo>
|
||||
<Input onChange={onInputChange} value={inputValue} ref={inputRef} />
|
||||
<Input
|
||||
onChange={onInputChange}
|
||||
value={inputValue}
|
||||
ref={inputRef}
|
||||
placeholder={t('placeholder')}
|
||||
/>
|
||||
</InputContainer>
|
||||
</Header>
|
||||
<Content>
|
||||
@@ -146,7 +157,6 @@ const Logo = styled.div`
|
||||
|
||||
const Input = styled.input.attrs({
|
||||
type: 'text',
|
||||
placeholder: 'search',
|
||||
autocomplete: 'off',
|
||||
autocorrect: 'off',
|
||||
autocapitalize: 'off',
|
||||
@@ -158,6 +168,7 @@ const Input = styled.input.attrs({
|
||||
text-align: center;
|
||||
font-family: monospace;
|
||||
font-size: 5rem;
|
||||
line-height: 1.2em;
|
||||
|
||||
${mobile} {
|
||||
font-size: 2rem;
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import { FaMapSigns } from 'react-icons/fa'
|
||||
import { FaGithub } from 'react-icons/fa'
|
||||
@@ -16,48 +17,47 @@ import { FaGem } from 'react-icons/fa'
|
||||
import { mobile } from '../util/css'
|
||||
|
||||
export default function Welcome() {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Hero>
|
||||
<Header>name new project</Header>
|
||||
<Text>
|
||||
namæ saves your time searching around registries and checking if the
|
||||
desired name is ready for use.
|
||||
</Text>
|
||||
<Header>{t('title')}</Header>
|
||||
<Text>{t('description')}</Text>
|
||||
</Hero>
|
||||
<List>
|
||||
<ListItem>
|
||||
<FaMapSigns /> Domains
|
||||
<FaMapSigns /> {t('providers.domains')}
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<FaGithub /> GitHub Organization
|
||||
<FaGithub /> {t('providers.github')}
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<FaNpm /> npm
|
||||
<FaNpm /> {t('providers.npm')}
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<FaPython /> PyPI
|
||||
<FaPython /> {t('providers.pypi')}
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<FaGem /> RubyGems
|
||||
<FaGem /> {t('providers.rubygems')}
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<DiRust /> Rust
|
||||
<DiRust /> {t('providers.rust')}
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<IoIosBeer /> Homebrew
|
||||
<IoIosBeer /> {t('providers.homebrew')}
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<FaJsSquare /> js.org
|
||||
<FaJsSquare /> {t('providers.jsorg')}
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<FaAws /> AWS S3 Bucket
|
||||
<FaAws /> {t('providers.s3')}
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<FaTwitter /> Twitter
|
||||
<FaTwitter /> {t('providers.twitter')}
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<FaSlack /> Slack
|
||||
<FaSlack /> {t('providers.slack')}
|
||||
</ListItem>
|
||||
</List>
|
||||
</Container>
|
||||
@@ -82,8 +82,12 @@ const Container = styled.div`
|
||||
|
||||
const Header = styled.h1`
|
||||
font-size: 3.5em;
|
||||
line-height: 0.8em;
|
||||
line-height: 1em;
|
||||
padding-bottom: 30px;
|
||||
|
||||
${mobile} {
|
||||
font-size: 3em;
|
||||
}
|
||||
`
|
||||
|
||||
const Text = styled.p`
|
||||
|
@@ -1,13 +1,15 @@
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { DiRust } from 'react-icons/di'
|
||||
import { Card } from '../Cards'
|
||||
import { DedicatedAvailability } from '../Availability'
|
||||
|
||||
export default function CratesioCard({ name }) {
|
||||
const { t } = useTranslation()
|
||||
const lowerCase = name.toLowerCase()
|
||||
|
||||
return (
|
||||
<Card title="Rust" key={lowerCase} nameList={[lowerCase]}>
|
||||
<Card title={t('providers.rust')} key={lowerCase} nameList={[lowerCase]}>
|
||||
{(name) => (
|
||||
<DedicatedAvailability
|
||||
name={name}
|
||||
|
@@ -1,14 +1,16 @@
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { FaMapSigns } from 'react-icons/fa'
|
||||
import { Card } from '../Cards'
|
||||
import { DedicatedAvailability } from '../Availability'
|
||||
|
||||
export default function DomainCard({ name }) {
|
||||
const { t } = useTranslation()
|
||||
const lowerCase = name.toLowerCase()
|
||||
|
||||
return (
|
||||
<Card
|
||||
title="Domain"
|
||||
title={t('providers.domains')}
|
||||
key={lowerCase}
|
||||
nameList={[`${lowerCase}.com`, `${lowerCase}app.com`, `${lowerCase}.app`]}
|
||||
alternativeList={[
|
||||
|
@@ -1,15 +1,17 @@
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { FaGithub } from 'react-icons/fa'
|
||||
import { Card } from '../Cards'
|
||||
import { DedicatedAvailability } from '../Availability'
|
||||
import { capitalize } from '../../util/text'
|
||||
|
||||
export default function GithubCard({ name }) {
|
||||
const { t } = useTranslation()
|
||||
const lowerCase = name.toLowerCase()
|
||||
|
||||
return (
|
||||
<Card
|
||||
title="GitHub Organization"
|
||||
title={t('providers.github')}
|
||||
key={name}
|
||||
nameList={[name]}
|
||||
alternativeList={[
|
||||
|
@@ -1,13 +1,18 @@
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { IoIosBeer } from 'react-icons/io'
|
||||
import { Card } from '../Cards'
|
||||
import { ExistentialAvailability } from '../Availability'
|
||||
|
||||
export default function HomebrewCard({ name }) {
|
||||
const { t } = useTranslation()
|
||||
const lowerCase = name.toLowerCase()
|
||||
|
||||
return (
|
||||
<Card title="Homebrew" key={lowerCase} nameList={[lowerCase]}>
|
||||
<Card
|
||||
title={t('providers.homebrew')}
|
||||
key={lowerCase}
|
||||
nameList={[lowerCase]}>
|
||||
{(name) => (
|
||||
<>
|
||||
<ExistentialAvailability
|
||||
|
@@ -1,13 +1,15 @@
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { FaJsSquare } from 'react-icons/fa'
|
||||
import { Card } from '../Cards'
|
||||
import { DedicatedAvailability } from '../Availability'
|
||||
|
||||
export default function JsOrgCard({ name }) {
|
||||
const { t } = useTranslation()
|
||||
const lowerCase = name.toLowerCase()
|
||||
|
||||
return (
|
||||
<Card title="js.org" key={lowerCase} nameList={[lowerCase]}>
|
||||
<Card title={t('providers.jsorg')} key={lowerCase} nameList={[lowerCase]}>
|
||||
{(name) => (
|
||||
<DedicatedAvailability
|
||||
name={`${name}.js.org`}
|
||||
|
@@ -1,14 +1,16 @@
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { FaNpm } from 'react-icons/fa'
|
||||
import { Card } from '../Cards'
|
||||
import { DedicatedAvailability } from '../Availability'
|
||||
|
||||
export default function NpmCard({ name }) {
|
||||
const { t } = useTranslation()
|
||||
const lowerCase = name.toLowerCase()
|
||||
|
||||
return (
|
||||
<Card
|
||||
title="npm"
|
||||
title={t('providers.npm')}
|
||||
key={lowerCase}
|
||||
nameList={[lowerCase]}
|
||||
alternativeList={[`${lowerCase}-js`]}>
|
||||
|
@@ -1,13 +1,16 @@
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { FaPython } from 'react-icons/fa'
|
||||
import { Card } from '../Cards'
|
||||
import { DedicatedAvailability } from '../Availability'
|
||||
import { capitalize } from '../../util/text'
|
||||
|
||||
export default function PypiCard({ name }) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<Card
|
||||
title="PyPI"
|
||||
title={t('providers.pypi')}
|
||||
key={name}
|
||||
nameList={[name]}
|
||||
alternativeList={[`Py${capitalize(name)}`]}>
|
||||
|
@@ -1,12 +1,15 @@
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { FaGem } from 'react-icons/fa'
|
||||
import { Card } from '../Cards'
|
||||
import { DedicatedAvailability } from '../Availability'
|
||||
|
||||
export default function RubyGemsCard({ name }) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<Card
|
||||
title="RubyGems"
|
||||
title={t('providers.rubygems')}
|
||||
key={name}
|
||||
nameList={[name]}
|
||||
alternativeList={[`${name.toLowerCase()}-rb`]}>
|
||||
|
@@ -1,13 +1,15 @@
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { FaAws } from 'react-icons/fa'
|
||||
import { Card } from '../Cards'
|
||||
import { DedicatedAvailability } from '../Availability'
|
||||
|
||||
export default function S3Card({ name }) {
|
||||
const { t } = useTranslation()
|
||||
const lowerCase = name.toLowerCase()
|
||||
|
||||
return (
|
||||
<Card title="AWS S3" key={lowerCase} nameList={[lowerCase]}>
|
||||
<Card title={t('providers.s3')} key={lowerCase} nameList={[lowerCase]}>
|
||||
{(name) => (
|
||||
<DedicatedAvailability
|
||||
name={name}
|
||||
|
@@ -1,13 +1,15 @@
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { FaSlack } from 'react-icons/fa'
|
||||
import { Card } from '../Cards'
|
||||
import { DedicatedAvailability } from '../Availability'
|
||||
|
||||
export default function SlackCard({ name }) {
|
||||
const { t } = useTranslation()
|
||||
const lowerCase = name.toLowerCase()
|
||||
|
||||
return (
|
||||
<Card title="Slack" key={lowerCase} nameList={[lowerCase]}>
|
||||
<Card title={t('providers.slack')} key={lowerCase} nameList={[lowerCase]}>
|
||||
{(name) => (
|
||||
<DedicatedAvailability
|
||||
name={name}
|
||||
|
@@ -1,13 +1,16 @@
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { FaTwitter } from 'react-icons/fa'
|
||||
import { Card } from '../Cards'
|
||||
import { DedicatedAvailability } from '../Availability'
|
||||
import { capitalize } from '../../util/text'
|
||||
|
||||
export default function TwitterCard({ name }) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<Card
|
||||
title="Twitter"
|
||||
title={t('providers.twitter')}
|
||||
key={name}
|
||||
nameList={[name]}
|
||||
alternativeList={[
|
||||
|
25
web/src/i18n.js
Normal file
25
web/src/i18n.js
Normal file
@@ -0,0 +1,25 @@
|
||||
import i18n from 'i18next'
|
||||
import Backend from 'i18next-xhr-backend'
|
||||
import LanguageDetector from 'i18next-browser-languagedetector'
|
||||
import { initReactI18next } from 'react-i18next'
|
||||
|
||||
i18n
|
||||
// load translation using xhr -> see /public/locales
|
||||
// learn more: https://github.com/i18next/i18next-xhr-backend
|
||||
.use(Backend)
|
||||
// detect user language
|
||||
// learn more: https://github.com/i18next/i18next-browser-languageDetector
|
||||
.use(LanguageDetector)
|
||||
// pass the i18n instance to react-i18next.
|
||||
.use(initReactI18next)
|
||||
// init i18next
|
||||
// for all options read: https://www.i18next.com/overview/configuration-options
|
||||
.init({
|
||||
fallbackLng: 'en',
|
||||
debug: false,
|
||||
interpolation: {
|
||||
escapeValue: false, // not needed for react as it escapes by default
|
||||
},
|
||||
})
|
||||
|
||||
export default i18n
|
@@ -1,10 +1,34 @@
|
||||
import React from 'react'
|
||||
import React, { Suspense } from 'react'
|
||||
import styled from 'styled-components'
|
||||
import ReactDOM from 'react-dom'
|
||||
import ReactGA from 'react-ga'
|
||||
import BarLoader from 'react-spinners/BarLoader'
|
||||
import App from './App'
|
||||
import * as serviceWorker from './serviceWorker'
|
||||
|
||||
ReactDOM.render(<App />, document.getElementById('root'))
|
||||
import './i18n'
|
||||
|
||||
const Fallback = () => (
|
||||
<Container>
|
||||
<BarLoader />
|
||||
</Container>
|
||||
)
|
||||
|
||||
const Container = styled.div`
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
`
|
||||
|
||||
ReactDOM.render(
|
||||
<Suspense fallback={<Fallback />}>
|
||||
<App />
|
||||
</Suspense>,
|
||||
document.getElementById('root')
|
||||
)
|
||||
|
||||
ReactGA.initialize('UA-28919359-15')
|
||||
ReactGA.pageview(window.location.pathname + window.location.search)
|
||||
|
Reference in New Issue
Block a user