7 #include "locale/japanese.h"
8 #include "locale/utf-8.h"
9 #include "util/enum-converter.h"
10 #include "util/string-processor.h"
11 #include "view/display-messages.h"
24 static const convert_key s2j_table[] = { { "mb", "nb" }, { "mp", "np" }, { "mv", "nv" }, { "mm", "nm" }, { "x", "ks" },
25 /* sindar:シンダール parantir:パランティア feanor:フェアノール */
26 { "ar$", "a-ru$" }, { "ir$", "ia$" }, { "or$", "o-ru$" }, { "ra", "ラ" }, { "ri", "リ" }, { "ru", "ル" }, { "re", "レ" }, { "ro", "ロ" }, { "ir", "ia" },
27 { "ur", "ua" }, { "er", "ea" }, { "ar", "aル" }, { "sha", "シャ" }, { "shi", "シ" }, { "shu", "シュ" }, { "she", "シェ" }, { "sho", "ショ" },
28 { "tha", "サ" }, { "thi", "シ" }, { "thu", "ス" }, { "the", "セ" }, { "tho", "ソ" }, { "cha", "ハ" }, { "chi", "ヒ" }, { "chu", "フ" }, { "che", "ヘ" },
29 { "cho", "ホ" }, { "dha", "ザ" }, { "dhi", "ジ" }, { "dhu", "ズ" }, { "dhe", "ゼ" }, { "dho", "ゾ" }, { "ba", "バ" }, { "bi", "ビ" }, { "bu", "ブ" },
30 { "be", "ベ" }, { "bo", "ボ" }, { "ca", "カ" }, { "ci", "キ" }, { "cu", "ク" }, { "ce", "ケ" }, { "co", "コ" }, { "da", "ダ" }, { "di", "ディ" },
31 { "du", "ドゥ" }, { "de", "デ" }, { "do", "ド" }, { "fa", "ファ" }, { "fi", "フィ" }, { "fu", "フ" }, { "fe", "フェ" }, { "fo", "フォ" }, { "ga", "ガ" },
32 { "gi", "ギ" }, { "gu", "グ" }, { "ge", "ゲ" }, { "go", "ゴ" }, { "ha", "ハ" }, { "hi", "ヒ" }, { "hu", "フ" }, { "he", "ヘ" }, { "ho", "ホ" },
33 { "ja", "ジャ" }, { "ji", "ジ" }, { "ju", "ジュ" }, { "je", "ジェ" }, { "jo", "ジョ" }, { "ka", "カ" }, { "ki", "キ" }, { "ku", "ク" }, { "ke", "ケ" },
34 { "ko", "コ" }, { "la", "ラ" }, { "li", "リ" }, { "lu", "ル" }, { "le", "レ" }, { "lo", "ロ" }, { "ma", "マ" }, { "mi", "ミ" }, { "mu", "ム" },
35 { "me", "メ" }, { "mo", "モ" }, { "na", "ナ" }, { "ni", "ニ" }, { "nu", "ヌ" }, { "ne", "ネ" }, { "no", "ノ" }, { "pa", "パ" }, { "pi", "ピ" },
36 { "pu", "プ" }, { "pe", "ペ" }, { "po", "ポ" }, { "qu", "ク" }, { "sa", "サ" }, { "si", "シ" }, { "su", "ス" }, { "se", "セ" }, { "so", "ソ" },
37 { "ta", "タ" }, { "ti", "ティ" }, { "tu", "トゥ" }, { "te", "テ" }, { "to", "ト" }, { "va", "ヴァ" }, { "vi", "ヴィ" }, { "vu", "ヴ" }, { "ve", "ヴェ" },
38 { "vo", "ヴォ" }, { "wa", "ワ" }, { "wi", "ウィ" }, { "wu", "ウ" }, { "we", "ウェ" }, { "wo", "ウォ" }, { "ya", "ヤ" }, { "yu", "ユ" }, { "yo", "ヨ" },
39 { "za", "ザ" }, { "zi", "ジ" }, { "zu", "ズ" }, { "ze", "ゼ" }, { "zo", "ゾ" }, { "dh", "ズ" }, { "ch", "フ" }, { "th", "ス" }, { "b", "ブ" },
40 { "c", "ク" }, { "d", "ド" }, { "f", "フ" }, { "g", "グ" }, { "h", "フ" }, { "j", "ジュ" }, { "k", "ク" }, { "l", "ル" }, { "m", "ム" }, { "n", "ン" },
41 { "p", "プ" }, { "q", "ク" }, { "r", "ル" }, { "s", "ス" }, { "t", "ト" }, { "v", "ヴ" }, { "w", "ウ" }, { "y", "イ" }, { "a", "ア" }, { "i", "イ" },
42 { "u", "ウ" }, { "e", "エ" }, { "o", "オ" }, { "-", "ー" }, { nullptr, nullptr } };
45 * @brief シンダリンを日本語の読みに変換する
46 * @param sindarin 変換前のシンダリン文字列
47 * @return std::string 変換後のシンダリン文字列
50 std::string sindarin_to_kana(std::string_view sindarin)
54 for (const auto &ch : sindarin) {
55 kana.push_back(isupper(ch) ? static_cast<char>(tolower(ch)) : ch);
59 for (auto idx = 0; s2j_table[idx].key1 != nullptr; idx++) {
60 concptr pat1 = s2j_table[idx].key1;
61 size_t len = strlen(pat1);
62 std::string::size_type i = 0;
64 while (i < kana.length()) {
65 if (strncmp(kana.data() + i, pat1, len) == 0) {
66 concptr pat2 = s2j_table[idx].key2;
68 kana.replace(i, len, pat2);
71 if (iskanji(kana[i])) {
79 kana.erase(kana.find('$'));
84 * 日本語動詞活用 (打つ>打って,打ち etc)
87 * OR : 殴る,蹴る > 殴ったり蹴ったり
89 static constexpr struct jverb_table_t {
90 std::string_view from;
91 std::string_view to_list[3];
93 { "する", { "し", "して", "した" } },
94 { "いる", { "いて", "いて", "いた" } },
96 { "える", { "え", "えて", "えた" } },
97 { "ける", { "け", "けて", "けた" } },
98 { "げる", { "げ", "えて", "げた" } },
99 { "せる", { "せ", "せて", "せた" } },
100 { "ぜる", { "ぜ", "ぜて", "ぜた" } },
101 { "てる", { "て", "てって", "てった" } },
102 { "でる", { "で", "でて", "でた" } },
103 { "ねる", { "ね", "ねて", "ねた" } },
104 { "へる", { "へ", "へて", "へた" } },
105 { "べる", { "べ", "べて", "べた" } },
106 { "める", { "め", "めて", "めた" } },
107 { "れる", { "れ", "れて", "れた" } },
109 { "う", { "い", "って", "った" } },
110 { "く", { "き", "いて", "いた" } },
111 { "ぐ", { "ぎ", "いで", "いだ" } },
112 { "す", { "し", "して", "した" } },
113 { "ず", { "じ", "じて", "じた" } },
114 { "つ", { "ち", "って", "った" } },
115 { "づ", { "ぢ", "って", "った" } },
116 { "ぬ", { "に", "ねて", "ねた" } },
117 { "ふ", { "ひ", "へて", "へた" } },
118 { "ぶ", { "び", "んで", "んだ" } },
119 { "む", { "み", "んで", "んだ" } },
120 { "る", { "り", "って", "った" } },
124 * @brief jverb_table_tに従って動詞を活用する
125 * @param in 変換元となる原形動詞
126 * @param type 変換種類を指定(AND/TO/OR)
129 std::string conjugate_jverb(std::string_view in, JVerbConjugationType type)
131 std::stringstream ss;
133 for (const auto &[from, to_list] : jverb_table) {
134 const auto stem_length = in.length() - from.length();
135 if (in.substr(stem_length) == from) {
136 ss << in.substr(0, stem_length) << to_list[enum2i(type)];
141 constexpr std::string_view conjuctions[3] = {
147 ss << in << conjuctions[enum2i(type)];
151 static const std::set<std::string_view> kinsoku_list{
153 "、", "。", ",", ".", "?", "!",
154 "ァ", "ィ", "ゥ", "ェ", "ォ", "ャ", "ュ", "ョ", "ッ",
155 "ぁ", "ぃ", "ぅ", "ぇ", "ぉ", "ゃ", "ゅ", "ょ", "っ",
157 "」", "』", ")", "}", "]", "》", "】",
162 * @brief 引数で与えられた文字が行頭禁則文字であるかどうか調べる
165 * @return 行頭禁則文字であるなら true、そうでないなら false
167 bool is_kinsoku(std::string_view ch)
169 return (ch.length() >= 2) && kinsoku_list.contains(ch);
173 * @brief 文字コードをSJISからEUCに変換する / Convert SJIS string to EUC string
174 * @param str 変換する文字列のポインタ
177 void sjis2euc(char *str)
180 unsigned char c1, c2;
182 int len = strlen(str);
184 std::vector<char> tmp(len + 1);
186 for (i = 0; i < len; i++) {
192 c1 = c1 * 2 - (c1 >= 0xe0 ? 0xe0 : 0x60);
195 c1 = c1 * 2 - (c1 >= 0xe0 ? 0xe1 : 0x61);
196 c2 += 0x60 + (c2 < 0x7f);
205 strcpy(str, tmp.data());
209 * @brief 文字コードをEUCからSJISに変換する / Convert EUC string to SJIS string
210 * @param str 変換する文字列のポインタ
213 void euc2sjis(char *str)
216 unsigned char c1, c2;
218 int len = strlen(str);
220 std::vector<char> tmp(len + 1);
222 for (i = 0; i < len; i++) {
228 c1 = (c1 >> 1) + (c1 < 0xdf ? 0x31 : 0x71);
229 c2 -= 0x60 + (c2 < 0xe0);
231 c1 = (c1 >> 1) + (c1 < 0xdf ? 0x30 : 0x70);
242 strcpy(str, tmp.data());
246 * @brief strを環境に合った文字コードに変換し、変換前の文字コードを返す。strの長さに制限はない。
247 * @param str 変換する文字列のポインタ
250 * 1: ASCII (Never known to be ASCII in this function.)<br>
254 byte codeconv(char *str)
257 for (auto i = 0; str[i]; i++) {
273 if (((0xa1 <= c1 && c1 <= 0xdf) || (0xfd <= c1 && c1 <= 0xfe)) && (0xa1 <= c2 && c2 <= 0xfe)) {
274 /* Only EUC is allowed */
281 else if (code != 2) {
286 auto is_cp932 = (0x81 <= c1 && c1 <= 0x9f) && ((0x40 <= c2 && c2 <= 0x7e) || (0x80 <= c2 && c2 <= 0xfc));
287 is_cp932 |= (0xe0 <= c1 && c1 <= 0xfc) && (0x40 <= c2 && c2 <= 0x7e);
292 /* Only SJIS is allowed */
299 else if (code != 3) {
323 /* Return kanji code */
328 * @brief 文字列sのxバイト目が漢字の1バイト目かどうか判定する
329 * @param s 判定する文字列のポインタ
330 * @param x 判定する位置(バイト)
331 * @return 漢字の1バイト目ならばTRUE
333 bool iskanji2(concptr s, int x)
337 for (i = 0; i < x; i++) {
342 if ((x == i) && iskanji(s[x])) {
350 * @brief 文字列の文字コードがASCIIかどうかを判定する
351 * @param str 判定する文字列へのポインタ
352 * @return 文字列の文字コードがASCIIならTRUE、そうでなければFALSE
354 static bool is_ascii_str(concptr str)
356 for (; *str; str++) {
358 if (!(0x00 < ch && ch <= 0x7f)) {
368 #include <initializer_list>
371 // UTF-8 の文字列長は必ずしも3バイトとは限らないが、変愚蛮怒の仕様範囲では3固定.
372 constexpr auto ENCODING_LENGTH = 3;
373 class EncodingConverter {
375 EncodingConverter(const std::initializer_list<unsigned char> from, const std::initializer_list<unsigned char> to)
381 bool equals(const unsigned char *p) const
383 return std::equal(from.begin(), from.end(), p);
386 void replace(unsigned char *p) const
388 std::copy_n(to.begin(), ENCODING_LENGTH, p);
392 std::vector<unsigned char> from;
393 std::vector<unsigned char> to;
396 const std::vector<EncodingConverter> encoding_characters = {
397 { { 0xef, 0xbd, 0x9e }, { 0xe3, 0x80, 0x9c } }, /* FULLWIDTH TILDE -> WAVE DASH (全角チルダ → 波ダッシュ) */
398 { { 0xef, 0xbc, 0x8d }, { 0xe2, 0x88, 0x92 } }, /* FULLWIDTH HYPHEN-MINUS -> MINUS SIGN (全角ハイフン → マイナス記号) */
402 * @brief 受け取ったUTF-8文字列を調べ、特定のコードポイントの文字の置き換えを行う
404 * 受け取ったUTF-8の文字列に含まれる文字を1つ1つ調べて、encoding_characters で
405 * 定義されている特定のコードポイントの文字の変換を行う。
407 * '~'と'-'は、Windows環境(CP932)とLinux/UNIX環境(EUC-JP)でUTF-8に対応する
408 * 文字としてそれぞれ別のコードポイントが割り当てられており、別の環境の
409 * UTF-8からシステムの文字コードに変換した時に、これらの文字は変換できず
412 * これを避けるため、Linux/UNIX環境(EUC-JP)ではUTF-8→EUC-JPの変換を行う前に
413 * 該当するコードポイントの文字をLinux/UNIX環境のものに置き換えてから
416 * @param str コードポイントの置き換えを行う文字列へのポインタ
418 static void ms_to_jis_unicode(char *str)
420 for (auto *p = (unsigned char *)str; *p; p++) {
422 if (0x00 < *p && *p <= 0x7f) {
426 if ((*p & 0xe0) == 0xc0) {
430 if ((*p & 0xf0) == 0xe0) {
431 for (const auto &converter : encoding_characters) {
432 if (converter.equals(p)) {
433 converter.replace(p);
440 if ((*p & 0xf8) == 0xf0) {
448 #elif defined(SJIS) && defined(WINDOWS)
454 * @brief 文字列の文字コードをUTF-8からEUC-JPに変換する
455 * @param utf8_str 変換元の文字列へのポインタ
456 * @param utf8_str_len 変換元の文字列の長さ(文字数ではなくバイト数)
457 * @param euc_buf 変換した文字列を格納するバッファへのポインタ
458 * @param euc_buf_len 変換した文字列を格納するバッファのサイズ
459 * @return 変換に成功した場合変換後の文字列の長さを返す
462 int utf8_to_euc(char *utf8_str, size_t utf8_str_len, char *euc_buf, size_t euc_buf_len)
464 static iconv_t cd = nullptr;
466 cd = iconv_open("EUC-JP", "UTF-8");
469 ms_to_jis_unicode(utf8_str);
471 size_t inlen_left = utf8_str_len;
472 size_t outlen_left = euc_buf_len;
476 if (iconv(cd, &in, &inlen_left, &out, &outlen_left) == (size_t)-1) {
480 return euc_buf_len - outlen_left;
484 * @brief 文字列の文字コードをEUC-JPからUTF-8に変換する
485 * @param euc_str 変換元の文字列へのポインタ
486 * @param euc_str_len 変換元の文字列の長さ(文字数ではなくバイト数)
487 * @param utf8_buf 変換した文字列を格納するバッファへのポインタ
488 * @param utf8_buf_len 変換した文字列を格納するバッファのサイズ
489 * @return 変換に成功した場合変換後の文字列の長さを返す
492 int euc_to_utf8(const char *euc_str, size_t euc_str_len, char *utf8_buf, size_t utf8_buf_len)
494 static iconv_t cd = nullptr;
496 cd = iconv_open("UTF-8", "EUC-JP");
499 size_t inlen_left = euc_str_len;
500 size_t outlen_left = utf8_buf_len;
501 const char *in = euc_str;
502 char *out = utf8_buf;
504 // iconv は入力バッファを書き換えないのでキャストで const を外してよい
505 if (iconv(cd, (char **)&in, &inlen_left, &out, &outlen_left) == (size_t)-1) {
509 return utf8_buf_len - outlen_left;
514 * @brief 文字コードがUTF-8の文字列をシステムの文字コードに変換する
515 * @param utf8_str 変換するUTF-8の文字列へのポインタ
516 * @param sys_str_buffer 変換したシステムの文字コードの文字列を格納するバッファへのポインタ
517 * @param sys_str_buflen 変換したシステムの文字コードの文字列を格納するバッファの長さ
518 * @return 変換に成功した場合TRUE、失敗した場合FALSEを返す
520 static bool utf8_to_sys(char *utf8_str, char *sys_str_buffer, size_t sys_str_buflen)
524 /* strlen + 1 を渡して文字列終端('\0')を含めて変換する */
525 return utf8_to_euc(utf8_str, strlen(utf8_str) + 1, sys_str_buffer, sys_str_buflen) >= 0;
527 #elif defined(SJIS) && defined(WINDOWS)
529 int input_len = strlen(utf8_str) + 1; /* include termination character */
531 std::vector<WCHAR> utf16buf(input_len);
533 /* UTF-8 -> UTF-16 */
534 if (MultiByteToWideChar(CP_UTF8, 0, utf8_str, input_len, utf16buf.data(), input_len) == 0) {
538 /* UTF-8 -> SJIS(CP932) */
539 if (WideCharToMultiByte(932, 0, utf16buf.data(), -1, sys_str_buffer, sys_str_buflen, nullptr, nullptr) == 0) {
551 * @brief システムの文字コードからUTF-8に変換する
552 * @param str システムの文字コードの文字列
553 * @return UTF-8に変換した文字列
554 * 変換に失敗した場合はstd::nullopt
556 std::optional<std::string> sys_to_utf8(std::string_view str)
559 std::string utf8str(str.length() * 2 + 1, '\0');
560 const auto len = euc_to_utf8(str.data(), str.length(), utf8str.data(), utf8str.size());
562 return (len >= 0) ? std::make_optional(std::move(utf8str.erase(len))) : std::nullopt;
563 #elif defined(SJIS) && defined(WINDOWS)
564 /* SJIS(CP932) -> UTF-16 */
565 std::vector<WCHAR> utf16buf(str.length());
566 const auto utf16_len = MultiByteToWideChar(932, 0, str.data(), str.size(), utf16buf.data(), utf16buf.size());
567 if (utf16_len == 0) {
571 /* UTF-16 -> UTF-8 */
572 std::vector<char> utf8buf(str.length() * 2 + 1);
573 const auto utf8_len = WideCharToMultiByte(CP_UTF8, 0, utf16buf.data(), utf16_len, utf8buf.data(), utf8buf.size(), nullptr, nullptr);
578 return std::make_optional<std::string>(utf8buf.data(), utf8_len);
585 * @brief 受け取った文字列の文字コードを推定し、システムの文字コードへ変換する
586 * @param strbuf 変換する文字列を格納したバッファへのポインタ。
587 * バッファは変換した文字列で上書きされる。
588 * UTF-8からSJISもしくはEUCへの変換を想定しているのでバッファの長さが足りなくなることはない。
589 * @param buflen バッファの長さ。
591 void guess_convert_to_system_encoding(char *strbuf, int buflen)
593 if (is_ascii_str(strbuf)) {
597 if (is_utf8_str(strbuf)) {
598 std::vector<char> work(buflen);
599 angband_strcpy(work.data(), strbuf, buflen);
600 if (!utf8_to_sys(work.data(), strbuf, buflen)) {
601 msg_print("警告:文字コードの変換に失敗しました");
608 * @brief 変愚蛮怒基準のポンド→キログラム変換定義(全体)
611 * @details 帝国ポンドとは完全にずれているが、気にするな!
613 static int lb_to_kg_all(int x)
619 * @brief 変愚蛮怒基準のポンド→キログラム変換定義(整数部)
623 int lb_to_kg_integer(int x)
625 return lb_to_kg_all(x) / 100;
629 * 変愚蛮怒基準のポンド→キログラム変換定義(小数部)
633 int lb_to_kg_fraction(int x)
635 return (lb_to_kg_all(x) % 100) / 10;