[OCI]Autonomous DatabaseからOCI AI Serviceを利用してテーブル内のテキストデータを各国語に翻訳してみた

# はじめに
Autonomous DataseからOCI AI ServiceのOCI Languageのテキスト翻訳機能を使用して、テキストデータを各国語に翻訳するPL/SQLストアド・ファンクションを作成してみました。

# 1. ストアド・ファンクションの作成

翻訳前のテキストの言語コード、翻訳後のテキストの言語コード、翻訳するテキストをパラメータとして渡すと、指定した言語コードに翻訳されたテキストを戻すPL/SQLファンクションtranslate_textを作成します。

2022/11時点で21の言語がサポートされているようで、その言語コードはそれぞれ以下のようになっています。

Arabic (ar)
Brazilian Portuguese (pt-BR)
Czech (cs)
Danish (da)
Dutch (nl)
English (en)
Finnish (fi)
French (fr)
Canadian French (fr-CA)
German (de)
Italian (it)
Japanese (ja)
Korean (ko)
Norwegian (no)
Polish (pl)
Romanian (ro)
Simplified Chinese(zh-CN)
Spanish (es-ww)
Swedish (sv)
Traditional Chinese (zh-TW)
Turkish (tr)

作成するファンクションの処理内容の詳細は、ソースコード内のコメントを参照してください。

なお、こちらのファンクションではOCI AI ServiceのOCI Lannguage APIにアクセスする際のクレデンシャルとしてAutonomous Databaseのリソース・プリンシパルを使用しています。

https://docs.oracle.com/cd//E83857_01/paas/autonomous-database/adbsa/resource-principal-about.html#GUID-7EED5198-2C92-41B6-99DD-29F4187CABF5

```sql
CREATE OR REPLACE FUNCTION translate_text(
    -- ソース（翻訳前）テキストの言語コード
    source_language_code VARCHAR2,
    -- ターゲット（翻訳語）テキストの言語コード
    target_language_code VARCHAR2,
    -- 翻訳対象のテキスト
    source_text          CLOB
)
RETURN CLOB
IS
    -- 変数の宣言
    region             VARCHAR2(100);
    body               CLOB;
    request_uri        VARCHAR2(200);
    resp               DBMS_CLOUD_TYPES.resp;
    translated_text    CLOB;
BEGIN
    -- リージョン識別子を取得 
    SELECT JSON_VALUE(cloud_identity, '$.REGION') INTO region FROM v$pdbs;
    -- APIコールのBODYを生成
    body := '{"documents":[{"key":"1","text":"'||source_text||'","languageCode":"'||source_language_code||'"}],';
    body := body||'"targetLanguageCode":"'||target_language_code||'"}';
    -- APIコール用のURIを生成
    request_uri := 'https://language.aiservice.'||region||'.oci.oraclecloud.com/20221001/actions/batchLanguageTranslation';
    -- APIをPOSTメソッドでコール
    resp := DBMS_CLOUD.SEND_REQUEST(
                credential_name => 'OCI$RESOURCE_PRINCIPAL',
                uri             => request_uri,
                method          => DBMS_CLOUD.METHOD_POST,
                body            => UTL_RAW.CAST_TO_RAW(body));
    -- APIコールのレスポンスから翻訳後のテキストを取得
    translated_text := JSON_VALUE(DBMS_CLOUD.GET_RESPONSE_TEXT(resp), '$.documents[0].translatedText');
    -- 翻訳後のテキストを戻り値として返す
    RETURN translated_text;
END;
/
```
SQL*PlusやDatabase Actionsから実行して、ファンクションtranslate_textを作成します。
```
SQL> CREATE OR REPLACE FUNCTION translate_text(
  2  	 -- ソース（翻訳前）テキストの言語コード
  3  	 source_language_code VARCHAR2,
  4  	 -- ターゲット（翻訳語）テキストの言語コード
  5  	 target_language_code VARCHAR2,
  6  	 -- 翻訳対象のテキスト
  7  	 source_text	      CLOB
  8  )
  9  RETURN CLOB
 10  IS
 11  	 region 	        VARCHAR2(100);
 12  	 body		        CLOB;
 13  	 request_uri	    VARCHAR2(200);
 14  	 resp		        DBMS_CLOUD_TYPES.resp;
 15  	 translated_text    CLOB;
 16  BEGIN
 17  	 -- リージョン識別子を取得
 18  	 SELECT JASON_VALUE(cloud_identity, '$.REGION') INTO region FROM v$pdbs;
 19  	 -- APIコールのBODYを生成
 20  	 body := '{"documents":[{"key":"1","text":"'||source_text||'","languageCode":"'||source_language_code||'"}],';
 21  	 body := body||'"targetLanguageCode":"'||target_language_code||'"}';
 22  	 -- APIコールのURIを生成
 23  	 request_uri := 'https://language.aiservice.'||region||'.oci.oraclecloud.com/20221001/actions/batchLanguageTranslation';
 24  	 -- APIをコール
 25  	 resp := DBMS_CLOUD.SEND_REQUEST(
 26  		     credential_name => 'OCI$RESOURCE_PRINCIPAL',
 27  		     uri	         => request_uri,
 28  		     method	         => DBMS_CLOUD.METHOD_POST,
 29  		     body	         => UTL_RAW.CAST_TO_RAW(body));
 30  	 -- APIコールのレスポンスから翻訳後のテキストを取得
 31  	 translated_text := JSON_VALUE(DBMS_CLOUD.GET_RESPONSE_TEXT(resp), '$.documents[0].translatedText');
 32  	 -- 翻訳後のテキストを戻り値として返す
 33  	 RETURN translated_text;
 34  END;
 35  /

ファンクションが作成されました。

SQL>
```
ファンクションtranslate_textが作成されました。

# 2. 作成したファンクションの動作確認

ファンクションが作成できたので、早速動作を確認してみます。

まずは「吾輩は猫である。」という日本語のテキストを英語に翻訳してみます。
翻訳前のテキストの言語コードとして「ja」、翻訳後のテキストの言語コードとして「en」、翻訳対象のテキストとして「吾輩は猫である。」を指定して、ファンクションtranslate_textを実行してみます。

```
SQL> set long 2000
SQL> SELECT translate_text('ja','en','我輩は猫である。') AS translated_text FROM dual;

TRANSLATED_TEXT
--------------------------------------------------------------------------------
I am a cat.

1行が選択されました。

SQL>
```
おぉ、日本語のテキスト「吾輩は猫である。」が英語に翻訳された結果としてテキスト「I am a cat.」が返ってきました！

次に、翻訳後のテキストの言語コードとして「fr」を指定して、フランス語に翻訳してみたいと思います。
```
SQL> SELECT translate_text('ja','fr','我輩は猫である。') AS translated_text FROM dual;

TRANSLATED_TEXT
--------------------------------------------------------------------------------
Je suis un chat.

1行が選択されました。

SQL>
```
指定通りに、「吾輩は猫である。」が日本語からフランス語に翻訳されました。

次は英語から日本語に翻訳してみましょう。
```
SQL> SELECT translate_text('en','ja','She sells sea shells by the sea shore. The shells she sells are surely sea shells. So if she sells shells on the sea shore, I''m sure she sells seashore shells.') AS translated_text FROM dual;

TRANSLATED_TEXT
--------------------------------------------------------------------------------
彼女は海岸で海殻を売っている。売っている貝はきっと海殻です。
海岸で貝を売るなら、きっと海岸の貝を売っているんだろう。


1行が選択されました。

SQL>
```
英語から日本語への翻訳も問題なくできました。

# 3. ファンクションを利用したテーブル内のテキストデータの翻訳


次にテキストデータを含むテーブルを作成して、テーブル内のテキストデータを翻訳してみたいと思います。
```
SQL> CREATE TABLE translate_test (
  2    id          NUMBER,
  3    source_text CLOB
  4  );

表が作成されました。

SQL> INSERT INTO translate_test VALUES (1, '吾輩は猫である。');

1行が作成されました。

SQL> INSERT INTO translate_test VALUES (2, '名前はまだない。');

1行が作成されました。

SQL> col source_text for a30
SQL> SELECT * FROM translate_test;

        ID SOURCE_TEXT
---------- ------------------------------
         1 吾輩は猫である。
         2 名前はまだない。

2行が選択されました。

SQL> 
```
テーブルの準備ができたので、テーブル内のテキストデータを翻訳してみます。
まずは英語に翻訳してみます。
```
SQL> col source_text for a30
SQL> col translated_text for a30
SQL> SELECT id, source_text,translate_text('ja','en',source_text) AS translated_text FROM translate_test;

	    ID SOURCE_TEXT			          TRANSLATED_TEXT
---------- ------------------------------ ------------------------------
         1 吾輩は猫である。                  I am a cat.
         2 名前はまだない。                  There is no name yet.

2行が選択されました。

SQL>
```
次はフランス語に翻訳してみます。
```
SQL> SELECT id, source_text,translate_text('ja','fr',source_text) AS translated_text FROM translate_test;

	    ID SOURCE_TEXT			          TRANSLATED_TEXT
---------- ------------------------------ ------------------------------
         1 吾輩は猫である。                  Je suis un chat.
         2 名前はまだない。                  Il n'y a pas encore de nom.

2行が選択されました。

SQL>
```
テーブル内のテキストデータがフランス語に翻訳されました。

# まとめ

Autonomous DatabaseからOCI AI ServiceのOCI Translateを利用して、テーブル内のテキストデータを各国語に翻訳することができました。
めでたし、めでたし。

# 参考情報
・[DBMS_CLOUD REST API](https://docs.oracle.com/cd//E83857_01/paas/autonomous-database/adbsa/dbms-cloud-subprograms.html#GUID-19B1639E-68E2-45BB-802C-817ABD0DBE88)
・[Language API](https://docs.oracle.com/en-us/iaas/api/#/en/language/20221001/)
・[BatchLanguageTranslation](https://docs.oracle.com/en-us/iaas/api/#/en/language/20221001/BatchLanguageTranslation/BatchLanguageTranslation)
・[BatchLanguageTranslationDetails Reference](https://docs.oracle.com/en-us/iaas/api/#/en/language/20221001/datatypes/BatchLanguageTranslationDetails)
・[BatchLanguageTranslationResult Reference](https://docs.oracle.com/en-us/iaas/api/#/en/language/20221001/datatypes/BatchLanguageTranslationResult)