質問: 'これはテストです コレハテストデス 神と神 123 髙﨑黑神福 (株) I II III 𩸽 傪'; これを、imutakaoruさんからいただいたコードで送信した結果は、以下の通りでした。 iPhone OK HUAWEI P8lite 「𩸽」が文字化け AL-Mail 𩸽 傪 とも文字化け ![イメージ説明](https://teratail-v2.storage.googleapis.com/uploads/contributed_images/131430c193da5bd1ff274f7c5ef19728.jpeg) ![(画像添付)](https://teratail-v2.storage.googleapis.com/uploads/contributed_images/6b1fff535325545e317341bb573d10cf.jpeg) HUAWEI P8liteは フォントが省略されているのではないかと思います。 「Al-Mail」で受信したメールを外部エディタで確認してみると、 本文はちゃんとBASE64でエンコードされていました。 別のサイトで同じ文字列をBASE64エンコードして比較したところ、 合致しましたので、エンコードの問題ではなさそうです。 Gmailからも同じ文字列を、Al-Mail宛に送ってみましたが、 やっはり同じように、最後の2字が化けました。 Al-Mailの問題か、フォントの問題かなと思います。 Al-Mailは古すぎるので、もうサポート外で良いのですが、 HUAWEI P8liteは、表示して欲しかったなぁ...。 先ほど引用させていただいた、この一言に尽きるわけですね...。 > > 文字エンコードを正しくコンバート出来る事と、 > > 端末で外字を表示出来る事はイコールではありません。 > > > UTF-8の全ての文字をサポートしようと思うと、 難しそうなので、(端末側のフォント事情になると、もうどうしようもない。) サポート文字範囲は、「ISO-2022-JP-MS」に含まれている文字まで、 それ以外はエンティティするか、ゲタ文字に置き換える、という仕様の方が安心できそうです。
回答: スクリーンショット追加ありがとうございます。リアルタイムで見れてないので、ほかの方と重複することも書くかもしれませんが、ともかく回答します。 「はしご高」などを送りたいのであれば、文字コードをUTF-8にしてしまうのが、プログラムを作る側にとっては簡単かと思います。しかしここでは、あえてキャラクタセットを「ISO-2022-JP」と指定しながらベンダ拡張の文字を符号化して送ることを考えます。 まず前提として、以下のコードには次の点が必須です。 * ソースのエンコーディングはUTF-8であること。 * ソースのエンコーディングと内部エンコーディングが一致していること。つまり、最初に`mb_internal_encoding('UTF-8')`が実行されていること。 * メール送信に`mb_send_mail()`ではなく`mail()`を使うこと。今回作成するメッセージは (後述の改行コードの違いを除き) MIME準拠の完全に符号化されたメッセージになるはずなので、送信時に更なる処理は必要ないと考えられます。 以下、工夫の説明と、ご質問のコードの修正点です。 ``` /* ご質問のコード */ // 件名のエンコード $subject = mb_convert_encoding($subject, "ISO-2022-JP", "UTF-8"); //UTF-8 → ISO-2022-JP $subject = mb_encode_mimeheader_ex($subject); ``` 他の方からも指摘がありますが、mb_convert_encoding()は不要です。またやはり指摘がある通り、標準のISO-2022-JPで変換すると非標準の文字は置換されてしまいます。拡張コデックを使うことにします。 ``` /* 修正後 */ $subject = mb_encode_mimeheader($subject, 'ISO-2022-JP-MS', 'B', "\n"); ``` ISO-2022-JP-MSで符号化しています。私の環境では結果は「=?ISO-2022-JP?B?......?=」のようになり、望みどおりのキャラクタセット名が入りました。 また、mb_encode_mimeheader()の第4引数は、ヘッダの折り返しに使う改行コードです。既定値の"\r\n"だと、mail()などを使って送るときに正常なメッセージが送れない場合があります。ご質問のコードでは別関数を作ってほかの対策も含めて行っているようですが、改行の調整だけでよいように思います。 次にアドレスフィールド。 ``` /* ご質問のコード */ // 差出人のエンコード $from_name = mb_convert_encoding($from_name, "ISO-2022-JP", "UTF-8"); //UTF-8 → ISO-2022-JP $from_name = mb_encode_mimeheader_ex($from_name); ``` 件名と同じです。ただし構造化ヘッダフィールドなので、phraseだけ符号化します。 ``` /* 修正後 */ // 差出人のエンコード $from_name = mb_encode_mimeheader($from_name, 'ISO-2022-JP-MS', 'B', "\n"); ``` そして、ヘッダフィールドを組み立てます。 ``` /* ご質問のコード */ // toをエンコード $to = "=?ISO-2022-JP?B?" . base64_encode($to) . '?= <' . $to . '>'; // fromをエンコード $from = "=?ISO-2022-JP?B?" . base64_encode($from_name) . '?= <' . $from_email . '>'; ``` ここで`$to`はASCIIしか含まないので、「符号化には最小の文字集合を使う」という原則により、ISO-2022-JPなどのより大きな文字集合による符号化はしません (たとえばteratailではユーザに送られるメッセージのヘッダに「=?UTF-8?Q?ikedas?=」などというものが入っていることがありますが、これは厳密には誤りです)。 一方`$from_name`は、さっき符号化したのでもう符号化しては**いけません**。 ``` /* 修正後 */ $to_name = str_replace("\"", "\\\"", str_replace("\\", "\\\\", $to)); $to = '"' . $to_name . '" <' . $to . '>'; $from = $from_name . ' <' . $from_email . '>'; ``` 最後に本文。 ``` /* ご質問のコード */ // 本文(planeパート)のエンコード $message = mb_convert_encoding($message, 'JIS', 'UTF-8'); $message = "=?ISO-2022-JP?B?" . base64_encode($message) . "?="; ``` 本文 (ボディ) での符号化にはcharsetやencodingが含まれません (ボディに関しては、そういったものはContent-TypeヘッダフィールドのCharsetパラメータとContent-Transfer-Encodingヘッダフィールドで指定します)。ヘッダフィールドの符号化 (RFC 2047) とボディの符号化 (RFC 2045) は別のものです。 そもそも、ISO-2022-JPは7ビット符号で、ボディでのBASE64符号化は不要です (RFC 1468を参照)。またこの場合、転送符号化は「Content-Transfer-Encoding: 7BIT」になります。 それと、上と同様、ISO-2022-JP-MSを使わないと非標準の文字が置換されてしまいます。 ``` /* 修正後 */ $message = mb_convert_encoding($message, 'ISO-2022-JP-MS', 'UTF-8'); ```