初心者がNext.jsでTypescriptを使ってDeepL APIによる翻訳機能を実装する。

2023/02/28に公開

はじめに

DeepL APIの実装方法についてまとめている記事が少なかったので、Next.jsでDeepL APIを使い、日本語の文章を英語に自動翻訳する機能の実装方法について自分なりにまとめました。

Next.jsを触り始めて3日目なので、私と同じ初学者との情報共有を目的とした記事です。

Next.jsに詳しい方で、気になるところがあれば気軽に指摘していただけると幸いです。

Next.jsアプリケーションを開始

まずはNext.jsのアプリケーションを開始します。開始の仕方は公式のドキュメントを参照してください。今回のプロジェクトではyarnを使用し、TypescriptとESLintをONにしました。
https://nextjs.org/docs/getting-started

アプリケーションの立ち上げが終わったら、code .でVSCodeを開きます。

アプリケーションが正常に開始されていることが確認できたら、pages/index.tsxファイルを開き、<main></main>タグの中身を空にします。そして、styles/Home.modules.cssファイルを開き、.mainセレクターの中身を

styles/Home.modules.css
.main {
  min-height: 100vh;
  background-color: white;
  color: black;
}

に書き換えてください。それ以外のセレクターは削除してしまって大丈夫です。

ターミナルでyarn devまたはnpm run devと叩き、localhostを立ち上げてブラウザ上で確認してみると、真っ白なページが表示されると思います。

次にpages/index.tsxファイルに戻り、<main></main>タグの中に以下を追加してみてください。

pages/index.tsx
<main>
  {/* 以下を追加 */}
  <div className={styles.content}>
    <button >英語に翻訳</button><br/><br/>
    <p>ここにテキストが入る</p>
  </div>
</main>

見やすいようにcssを追加します。

styles/Home.modules.css
.content {
  margin: 0 auto;
  width: 60%;
  padding-top: 100px;
}

ブラウザで確認すると以下のようになってると思います。

これでNext.jsの準備は終わりです。今回はコンポーネントなどは使わず、pages/index.tsxページのみで実装します。

DeepL APIの登録と準備

まず、DeepL APIのサイトを開きます。

「無料で登録する」ボタンをクリックと、DeepL API Free か DeepL API Pro を選ぶセクションにスクロールします。今回は DeepL API Free を使うので DeepL API Free の「無料で登録する」をクリックしてください。

DeepL API Pro を選択すると、多少コストが増えますが、翻訳機能を無制限に使うことができます。対して、DeepL API Freeは月々500,000文字までという制限がつきます。

「無料で登録する」ボタンをクリックすると、アカウント情報を入力するページに遷移するので案内に従って情報を入力してください。途中でカード情報を聞かれますが、セキュリティのための登録で、支払い請求が来ることはありません。

DeepL API の登録はこれで終了です。
DeepL API の使用状況やDeepL APIを使う上で大事な「認証キー」はアカウントページからみることができます。今後使うので開いておいてください。

DeepL ライブラリのインストール

今回はより簡単に実装するためにdeeplライブラリを使用しました。

https://github.com/funkyremi/deepl

VSCodeでターミナルを開きctrl+cで一旦localhostを中断し、yarn add deepl --saveまたはnpm i deepl --saveと叩き、deeplライブラリをインストールします。

※他にもdeepl-nodeというライブラリがありますが、サーバーサイドでの実装は上手く行ったものの、なぜかクライアントサイドでの実装でエラーが出るので、やり方わかる人いたらコメントください😭

DeepL APIによる自動翻訳関数の実装

それではDeepL API による日本語→英語の自動翻訳機能を実装します。

まず、プロジェクトのルートにlibsというフォルダーを作成し、libsの中に、Translation.tsxファイルを作成します。

Translation.tsxファイルを開き、deeplライブラリをインポートします。

ここでdeeplライブラリのドキュメントを確認してみると、

translate({
    text: 'I am a text',
    target_lang: 'FR',
    auth_key: 'authkey',
    // All optional parameters available in the official documentation can be defined here as well.
  })
  .then(result => {
      console.log(result.data);
  })
  .catch(error => {
      console.error(error)
  });

translate()という関数があることがわかります。まず、それをインポートしましょう。

libs/Translation.tsx
import translate from 'deepl'

そして、translate()関数を使うにはtexttarget_langauth_keyの3つの引数が最低限必要であることが記されています。今回はTypescriptでの実装なのでそれぞれのtypeを見てみると、text: stringtarget_lang:DeeplLanguagesauth_key:stringになっています。target_langのtypeがDeeplLanguagesになっている理由は、サポートしている言語コード以外の言語コードが入力された場合にエラーを返すためだと考えられます。

このtype DeeplLanguagesもdeeplライブラリからインポートすることができます。translateのインポートに{ DeeplLanguages }を追加してください。

libs/Translation.tsx
import translate, { DeeplLanguages } from 'deepl'

https://github.com/funkyremi/deepl

次にdeeplライブラリの引数であるauth_keyを設定します。このauth_keyはDeepL APIから情報を取得するときに必要な認証キーで、外部に漏れてはいきません。このような情報は環境変数として.env.localファイルに書いておく必要があります。

プロジェクトのルートに.env.localファイルを新しく作成してください。

続いて、DeepL APIのアカウントページにいきアカウントタブをクリックしてください。
下にスクロールすると「DeepL APIで使用する認証キー」というセクションがあるので、認証キーをコピーしてください。

.env.localファイルに戻り、以下の通りに書いてください。

.env.local
NEXT_PUBLIC_DEEPL_AUTH_KEY=認証キー

※頭にNEXT_PUBLIC_がないと上手くアクセスできませんでした。

認証キーにクオテーションマークや()などは必要ありません。
process.env.NEXT_PUBLIC_DEEPL_AUTH_KEYでこの認証キーにアクセスすることができます。libs/Translation.tsxで変数として呼んでみましょう。

libs/Translation.tsx
const auth_key:string = (process.env.NEXT_PUBLIC_DEEPL_AUTH_KEY === undefined)? "":process.env.NEXT_PUBLIC_DEEPL_AUTH_KEY;

ここで、auth_keyのtypeがstringなのに対し、process.env.NEXT_PUBLIC_DEEPL_AUTH_KEYのtypeがstring | undefinedなので、process.env.NEXT_PUBLIC_DEEPL_AUTH_KEYがundefinedの場合は空文字を返すような実装にしました。

※もっといい方法あったらコメントで教えてください

次に、DeepL APIと通信をして翻訳したテキストを返す関数を非同期処理、async functionで実装します。今回の関数は引数としてmy_text: string my_target_lang: DeeplLanguagesを取ります。また、今回は DeepL API Free での実装なのでtranslate()関数のoptional引数であるfree_apitrueに設定する必要があります。

deeplライブラリのドキュメントを確認すると、translate()関数の返り値の例は以下のようになります。

{
    "translations": [
        {
            "detected_source_language": "EN",
            "text": "Je suis un texte"
        }
    ]
}

今回の関数を呼ぶと

{
    "detected_source_language": DeeplLanguages,
    "text": string,
}

という値にアクセスできるように実装します。以下が実装した関数です。

libs/Translation.tsx
export async function Translator(text:string, target:DeeplLanguages) {
    const res = await translate({free_api:true, text: text, target_lang:target, auth_key:auth_key!})
    const result = res.data.translations
    return result[0]
  }

このTranslator関数をpages/index.tsxにインポートすれば自動翻訳を実装することができます。

自動翻訳関数を使ってみる

pages/index.tsxに移動してください。
まず、Translator関数をインポートします。また、type DeeplLanguagesも使いたいので、pages/index.tsxの方でもインポートします。

pages/index.tsx
import { Translator } from '@/libs/Translation'
import { DeeplLanguages } from 'deepl'

続いて、Home()内にuseState()を使って値と更新のための関数を宣言します。useStateのインポートは自動でされると思いますが、されない場合は忘れずに行なってください。

pages/index.tsx
import Head from 'next/head'
import styles from '@/styles/Home.module.css'
import { Translator } from '@/libs/Translation'
import { useState } from 'react'

export default function Home() {
  const [myText, setMyText] = useState('')
  return (
    <>
      ...
    </>
  )
}

次に、ボタンを押した時のクリック関数を作り、その中でTranslator関数と、その返り値でsetMyText関数を実行します。

ここで注意しなければいけないことがあります。Translator()関数の返り値のtypeは

{
    "detected_source_language": DeeplLanguages,
    "text": string,
}

だと思われがちなのですが、async functionで非同期処理を行う場合の返り値はPromise<>オブジェクトで囲われてしまいます。このPromise<>オブジェクトから欲しい情報を取り出すには、ライブラリのドキュメントにある通り、.then()関数の中で処理を行う必要があります。

translate({
    text: 'I am a text',
    target_lang: 'FR',
    auth_key: 'authkey',
    // All optional parameters available in the official documentation can be defined here as well.
  })
  .then(result => {
      console.log(result.data);
  })
  .catch(error => {
      console.error(error)
  });

それでは、クリック関数を実装していきます。

pages/index.tsx
import Head from 'next/head'
import styles from '@/styles/Home.module.css'
import { Translator } from '@/libs/Translation'
import { DeeplLanguages } from 'deepl'
import { useState } from 'react'

export default function Home() {
  const [myText, setMyText] = useState('')

  function clickTranslate(my_text:string, my_target_lang:DeeplLanguages) {
    const translations = Translator(my_text, my_target_lang)
    translations.then((result) => setMyText(result.text))
  }
  return (
    <>
      ...
    </>
  )
}

前述した通り、then()関数の中で

{
    "detected_source_language": DeeplLanguages,
    "text": string,
}

resultとしてを受け取り、result.textsetMyText()関数の引数に代入しています。

このクリック関数を<button></button>タグの中のonClickアトリビュートで呼びます。引数に代入する値はmyTextと、今回は日本語→英語の翻訳なので、言語コードとして設定されている'EN-US'にします。

pages/index.tsx
<main className={styles.main}>
        {/* ここを追加 */}
        <div className={styles.content}>
          <button onClick={() => clickTranslate(myText, 'EN-US')}>英語に翻訳</button><br/><br />
          <p>ここにテキストが入る</p>
        </div>
      </main>

最後にmyTextの初期値を適当に設定して翻訳されるか確かめたいと思います。<p></p>タグの中身も忘れずに{myText}に変えておいてください。

pages/index.tsx
import Head from 'next/head'
import styles from '@/styles/Home.module.css'
import { Translator } from '@/libs/Translation'
import { DeeplLanguages } from 'deepl'
import { useState } from 'react'

export default function Home() {
  // ここを追加
  const templeteText = "翻訳とは、ある形で表現されている対象を、異なる形で改めて表現する行為である。\
  特に、自然言語において、起点言語による文章を、別の目標言語による文章に変換する行為をさす。例えば、\
  英語文から日本語文へ翻訳された場合は、起点言語が英語であり、目標言語が日本語である。起点言語による文を原文といい、\
  目標言語による文を訳文・翻訳文と言う。一方で、プログラミング用語としては形式言語の変換という意味でも用いられる。なお、\
  文ではなく発話を翻訳する行為は、通訳とも呼ばれる。"
  
  // useState()の中にtempleteTextを入れる。
  const [myText, setMyText] = useState(templeteText)

  function clickTranslate(my_text:string, my_target_lang:DeeplLanguages) {
    const translations = Translator(my_text, my_target_lang)
    translations.then((result) => setMyText(result.text))
  }
  return (
    <>
      <Head>
        <title>Create Next App</title>
        <meta name="description" content="Generated by create next app" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <main className={styles.main}>
        <div className={styles.content}>
          <button onClick={() => clickTranslate(myText, 'EN-US')}>英語に翻訳</button><br/><br />
	  {/* ここを変更 */}
          <p>{myText}</p>
        </div>
      </main>
    </>
  )
}

yarn devまたはnpm run devで実行し、ブラウザで確認してみましょう。最初は日本語の文章が表示されているはずです。

ボタンを押して翻訳されるか確認してみましょう。

無事翻訳されました。

以上でDeepL APIによる日本語→英語の自動翻訳機能の実装方法の紹介を終わります。

全コード↓

libs/Translation.tsx
import translate, { DeeplLanguages } from 'deepl'

const auth_key:string = (process.env.NEXT_PUBLIC_DEEPL_AUTH_KEY === undefined)? "":process.env.NEXT_PUBLIC_DEEPL_AUTH_KEY;

export async function Translator(my_text:string, my_target_lang:DeeplLanguages) {
    const res = await translate({free_api:true, text: my_text, target_lang:my_target_lang, auth_key:auth_key})
    const result = res.data.translations
    return result[0]
  }
.env.local
NEXT_PUBLIC_DEEPL_AUTH_KEY=認証キー
pages/index.tsx
import Head from 'next/head'
import styles from '@/styles/Home.module.css'
import { Translator } from '@/libs/Translation'
import { DeeplLanguages } from 'deepl'
import { useState } from 'react'

export default function Home() {
  const templeteText = "翻訳とは、ある形で表現されている対象を、異なる形で改めて表現する行為である。\
  特に、自然言語において、起点言語による文章を、別の目標言語による文章に変換する行為をさす。例えば、\
  英語文から日本語文へ翻訳された場合は、起点言語が英語であり、目標言語が日本語である。起点言語による文を原文といい、\
  目標言語による文を訳文・翻訳文と言う。一方で、プログラミング用語としては形式言語の変換という意味でも用いられる。なお、\
  文ではなく発話を翻訳する行為は、通訳とも呼ばれる。"
  
  const [myText, setMyText] = useState(templeteText)

  function clickTranslate(my_text:string, my_target_lang:DeeplLanguages) {
    const translations = Translator(my_text, my_target_lang)
    translations.then((result) => setMyText(result.text))
  }
  return (
    <>
      <Head>
        <title>Create Next App</title>
        <meta name="description" content="Generated by create next app" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <main className={styles.main}>
        <div className={styles.content}>
          <button onClick={() => clickTranslate(myText, 'EN-US')}>英語に翻訳</button><br/><br />
          <p>{myText}</p>
        </div>
      </main>
    </>
  )
}

最後に

初めてZennで記事を書いてみたのですが、いかがだったでしょうか。
このやり方を使って、microCMSのブログを自動翻訳する機能も実装したので、需要があったら記事にします。

もっともっと成長していきたいので、ぜひコメントで厳しいご指摘をください。
最後まで読んでいただきありがとうございました。

Discussion