絵文字Translater作ってみた🐈‍⬛🐈


# はじめに

まったく何の役にも立ちませんが、

絵文字翻訳ツール的なものを作成してみました💪

# 今回作った絵文字翻訳ツール

* 絵文字＝＞英語名に変換

![絵文字Translater作ってみた_03.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3777149/628b39e2-6509-53d0-7af9-ead97ff99e1b.jpeg)

![絵文字Translater作ってみた_04.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3777149/1e4ae10e-6fa8-ac9c-c9e4-f68ba0699509.jpeg)

* 絵文字＝＞日本語名に変換

![絵文字Translater作ってみた_03.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3777149/628b39e2-6509-53d0-7af9-ead97ff99e1b.jpeg)

![絵文字Translater作ってみた_05.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3777149/e1f55eb0-48e9-1a3c-aa1d-3f8b100f5a25.jpeg)

* 名称＝＞絵文字に変換

![絵文字Translater作ってみた.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3777149/da4611e2-3836-edbb-9498-6933a5d29211.jpeg)

![絵文字Translater作ってみた_02.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3777149/8dbdd97b-9fa1-20c5-2237-7e8b331bb397.jpeg)

* 説明モード(絵文字のあとに説明を入れる)

![絵文字Translater作ってみた_06.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3777149/eac2a649-d900-35a1-3b1c-227e8129f3b3.jpeg)

* DarkMode

![絵文字Translater作ってみた_08.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3777149/699cf765-e3dd-ffa6-c0cd-184bf974b8a5.jpeg)

* (謎に、絵文字がゆっくり流れる背景アニメーションがあります)

![絵文字Translater作ってみた.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3777149/745b2dd3-4140-1dfc-6bc6-7275a3b346a2.gif)

## 成果物のURL

https://shisojuice.github.io/EmojiTranslater/

# プロジェクト構成など

## 絵文字について

UnicodeコンソーシアムのFull Emoji Listの下記を参考に作成

https://unicode.org/emoji/charts/full-emoji-list.html

下記の記事が絵文字の勉強になりました🙇‍♂️

https://zenn.dev/cybozu_frontend/articles/how_to_propose_a_new_emoji

## 力技で実装していく！💪

(生成AI時代なのに、力技で実装しています。まったく参考になりませんのでご了承ください。🙇‍♂️)

linderaというRust製の日本語形態素解析器を使ってみたかったので、

https://qiita.com/mosuka/items/0fdaaf91f5530d427dc7

一部ロジックはRust(Wasm)で実装していきます。

まず、翻訳のデータを作成。

UnicodeコンソーシアムのFull Emoji ListのCLDR Short Nameを日本語訳し、Jsonのstring形式にする

```rust:emoji_json.rs
ub fn get_json() -> &'static str { r#"[
{"cd":"U+1F600","nm":"grinning face","jp":"ニヤニヤ顔"},
{"cd":"U+1F603","nm":"grinning face with big eyes","jp":"大きな目のニヤニヤ顔"},
//略
]"#
}

```

次に。置換する関数を作成。

###  replace_emoji_with_name 関数

文字列、language、explainフラグを受け取り、絵文字をShortNameに置き換えた新しい文字列を返す。

すべての文字がUnicode絵文字の範囲（U+1F000からU+1F9FF）内にあるかどうかを確認し、

もしすべての文字が絵文字の範囲内であれば、単体の絵文字として扱う。🐈

g.chars().count() > 1：g が複数の文字を含むかどうかを確認し、🐈‍⬛

複数の文字を含む場合、最初の文字が絵文字の範囲内にあるかどうかを確認。

最初の文字が絵文字の範囲内であれば、合体絵文字として扱う。

上記外はそのまま文字列として扱う。


### get_short_name 関数

絵文字文字列、language、explainフラグを受け取り、ShortNameを返す。

format_emoji_to_unicode関数でemojiをUnicode形式の文字列に変換します（例： "U+1F600"）。

JSONデータから絵文字情報を読み込み、Unicodeコードが一致するEmojiRowをフィルタリングする。

languageに応じて、英名、日本語名を取得する。

explain フラグが true の場合、絵文字直後に説明を追加する（例： "😀(*grinning face*)")。

explain フラグが false の場合、説明のみを返す（例： "(grinning face)")。


```rust:lib.rs
//略

#[wasm_bindgen]
pub fn replace_emoji_with_name(text: &str,language: u32,explain: bool) -> String {
    text.graphemes(true)
        .map(|g| {
            if g.chars().all(|c| (0x1F000..=0x1F9FF).contains(&(c as u32))) {
                //単体絵文字
                get_short_name(g, language,explain)
            } else {
                if g.chars().count() > 1 {
                    if let Some(c) = g.chars().next() {
                        if (0x1F000..=0x1F9FF).contains(&(c as u32)) {
                            //合体絵文字
                            get_short_name(g, language,explain)
                        } else { g.to_string() }
                    } else { g.to_string() }
                } else { g.to_string() }
            }
        })
        .collect()
}

/// emoji_unicodeに紐づくshort_nameにして返します(explainの時は絵文字直後に説明を入れる)
fn get_short_name(emoji :&str,language: u32,explain: bool) -> String {
    let mut result = String::new();
    let unicode = format_emoji_to_unicode(emoji);

    let emoji_rows: Vec<EmojiRow> = from_str(get_json()).unwrap();
    let objs = emoji_rows.iter()
        .filter(|item| item.cd.as_str() == unicode);
    for obj in objs {
        if language == 0 {
            result= obj.clone().nm;
        }
        if language == 1 {
            result= obj.clone().jp;
        }
        break;
    }

    if explain {
        let mut result_explain = String::new();
        result_explain.push_str(emoji);
        result_explain.push_str(&* format!("(*{}*)",result));
        result_explain
    }
    else {
        format!("(*{}*)",result)
    }
}

/// emojiをunicodeにformatして返します
fn format_emoji_to_unicode(emoji :&str) -> String {
    let mut unicode = String::new();
    let mut i = 0;
    for c in emoji.chars()
    {
        if i>0 {unicode.push_str(" ")};
        unicode.push_str(&format!("U+{:X}", c as u32));
        i = i + 1;
    }
    unicode
}
//略

```
### replace_name_with_emoji 関数

文字列、explainフラグを受け取り、テキスト内の単語を絵文字に置き換える。

関数は、Tokenizerを使用してテキストを形態素解析し、各トークンをget_emoji関数に渡して絵文字に変換する。

### get_emoji 関数

文字列、explainフラグを受け取り、与えられた単語に対応する絵文字を検索。

関数は、EmojiRowのリストをループし、英名または日本名がtextと一致する行を探し、

一致する行が見つかった場合、format_unicode_to_emoji関数でUnicodeコードを絵文字に変換し（例："U+1F600"）、

explainフラグに応じて元の単語を追加。

一致する絵文字が見つからない場合、元の単語がそのまま返す。


```rust:lib.rs
//略

#[wasm_bindgen]
pub fn replace_name_with_emoji(text: &str,explain: bool) -> String {
    let mut result = String::new();
    let dictionary = DictionaryConfig {
        kind: Some(DictionaryKind::IPADIC),
        path: None,
    };
    let config = TokenizerConfig {
        dictionary,
        user_dictionary: None,
        mode: Mode::Normal,
    };
    let tokenizer = Tokenizer::from_config(config).unwrap();
    let tokens = tokenizer.tokenize(text).unwrap();
    for token in tokens {
        result.push_str(&*get_emoji(token.text,explain));
    }
    result
}

/// short_name(類似名含む名詞単位)に紐づくemojiにして返します
fn get_emoji(text: &str,explain: bool) -> String {
    let mut result = String::new();
    let emoji_rows: Vec<EmojiRow> = from_str(get_json()).unwrap();
    for row in emoji_rows {
       // short_name
       if row.nm == text {
           if explain { result.push_str(text); }
           result.push_str(&*format_unicode_to_emoji(&*row.cd));
           break;
       }
       // short_name_jp
       if row.jp == text {
           if explain { result.push_str(text); }
            result.push_str(&*format_unicode_to_emoji(&*row.cd));
            break;
       }
    }
    if result.is_empty() {
        result.push_str(text);
    }
    result
}

/// unicodeをemojiにformatして返します
fn format_unicode_to_emoji(unicode: &str) -> String {
    let result
        = unicode.split_whitespace().map(|code| {
            if let Some(stripped) = code.strip_prefix("U+") {
                if let Ok(hex) = u32::from_str_radix(stripped, 16) {
                    char::from_u32(hex).unwrap_or_default().to_string()
                } else {
                    code.to_string()
                }
            } else {
                code.to_string()
            }
        })
        .collect::<String>();
    result
}
//略

```

ソース

https://github.com/shisojuice/EmojiTranslater


# まとめ

絵文字翻訳ツールを作ってみたが、お披露目するところがなかったので、

クソアプリとして供養いたしました。😊

自分の作ってみたいものを、

力技や自分の好きなように実装するのは、とっても楽しい！💪