2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Next.jsをRosettaで多言語対応したメモ

Last updated at Posted at 2020-05-02

概要

Next.jsの多言語化。
Rosetta ExampleをTypescriptで書いてみたメモ。

この時点のソース

// 追記
型を付けたメモで、型をちゃんと付けたので、そちらも参照のこと。

ソース

lib/i18n.tsx
import { createContext, useState, useRef, useEffect } from 'react'
import rosetta from 'rosetta'

const i18n = rosetta()

export const defaultLanguage = 'ja'
export const languages = ['ja', 'en']
export const contentLanguageMap = { ja: 'ja-JP', en: 'en-US' }

export const I18nContext = createContext(null)

// default language
i18n.locale(defaultLanguage)

export default function I18n({ children, locale, lngDict }) {
  const [activeDict, setActiveDict] = useState(() => lngDict)
  const activeLocaleRef = useRef(locale || defaultLanguage)
  const [, setTick] = useState(0)
  const firstRender = useRef(true)

  // for initial SSR render
  if (locale && firstRender.current === true) {
    firstRender.current = false
    i18n.locale(locale)
    i18n.set(locale, activeDict)
  }

  useEffect(() => {
    if (locale) {
      i18n.locale(locale)
      i18n.set(locale, activeDict)
      activeLocaleRef.current = locale
      // force rerender
      setTick((tick) => tick + 1)
    }
  }, [locale, activeDict])

  const i18nWrapper = {
    activeLocale: activeLocaleRef.current,
    t: (...args) => i18n.t(...(args as [any, ...any[]])),
    locale: (l, dict) => {
      i18n.locale(l)
      activeLocaleRef.current = l
      if (dict) {
        i18n.set(l, dict)
        setActiveDict(dict)
      } else {
        setTick((tick) => tick + 1)
      }
    },
  }

  return (
    <I18nContext.Provider value={i18nWrapper}>{children}</I18nContext.Provider>
  )
}
hooks/use-i18n.tsx
import { useContext } from 'react'
import { I18nContext } from '~/lib/i18n'

export default function useI18n() {
  const i18n = useContext(I18nContext)
  return i18n
}
pages/_app.tsx
import * as React from 'react'
import { Provider } from 'react-redux'
import App from 'next/app'
import { ThemeProvider } from '@material-ui/core/styles'
import CssBaseline from '@material-ui/core/CssBaseline'
import Head from 'next/head'
import { setupStore } from '~/store'
import theme from '~/theme'
import '~/styles/global.css'
+ import I18n from '~/lib/i18n'

const store = setupStore()

class MyApp extends App {
  componentDidMount() {
    // Remove the server-side injected CSS.
    const jssStyles = document.querySelector('#jss-server-side')
    if (jssStyles) {
      jssStyles.parentElement.removeChild(jssStyles)
    }
  }
  public render() {
    const { Component, pageProps } = this.props
    return (
      <Provider store={store}>
        <React.Fragment>
          <Head>
            <title>Create Now</title>
            <link rel="icon" href="/favicon.ico" />
          </Head>
          <ThemeProvider theme={theme}>
            {/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
            <CssBaseline />
+            <I18n lngDict={pageProps.lngDict} locale={pageProps.lng}>
              <Component {...pageProps} />
+            </I18n>
          </ThemeProvider>
        </React.Fragment>
      </Provider>
    )
  }
}

export default MyApp
pages/lostrpg/index.tsx
import { useEffect } from 'react'
import { NextPage } from 'next'
import Head from 'next/head'
import Link from '~/components/atoms/mui/Link'
import Container from '~/components/organisms/lostrpg/LostrpgContainer'
+ import useI18n from '~/hooks/use-i18n'
+ import { contentLanguageMap } from '~/lib/i18n'
+ import EN from '~/locales/en.json'
+ import JA from '~/locales/ja.json'

const Page: NextPage = () => {
+ const i18n = useI18n()


+  useEffect(() => {
+    i18n.locale('ja', JA)
+  }, [])
  return (
    <Container>
      <Head>
        <meta
+          httpEquiv="content-language"
+          content={contentLanguageMap[i18n.activeLocale]}
        />
+        <title>{i18n.t('lostrpg.index.title')}</title>
      </Head>
      <h2>{i18n.t('lostrpg.index.title')}</h2>
      <div style={{ padding: '5px' }}>
        <a
          href="#"
          onClick={() => {
+           i18n.locale('en', EN)
          }}
        >
          English
        </a>

        <a
          href="#"
          style={{ marginLeft: '10px' }}
          onClick={() => {

+           i18n.locale('ja', JA)
          }}
        >
          日本語
        </a>
      </div>
      <ul>
        <li>
          <Link href="/lostrpg/camps/list">
+            {i18n.t('lostrpg.index.campList')}
          </Link>
        </li>
      </ul>

      <Link href="/"> {i18n.t('common.back')}</Link>
    </Container>
  )
}

export default Page

参考

TypeScript + React で i18n (国際化/多言語) 対応を楽して続けるためのアレコレ
TypeScriptで最低n個の要素を持った配列の型を宣言する方法
rosetta

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?