diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0ae21ee --- /dev/null +++ b/.gitignore @@ -0,0 +1,35 @@ +*.annot +*.cmo +*.cma +*.cmi +*.a +*.o +*.cmx +*.cmxs +*.cmxa + +# ocamlbuild working directory +_build/ + +# ocamlbuild targets +*.byte +*.native + +# oasis generated files +setup.data +setup.log + +# Merlin configuring file for Vim and Emacs +.merlin + +# Dune generated files +*.install + +# Local OPAM switch +_opam/ + +# node_modules +node_modules/ + +# dotenv files +.env \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..6a1551c --- /dev/null +++ b/README.md @@ -0,0 +1,20 @@ +# アルベド語自動翻訳サイト + +https://proof-ninja.github.io/albhed/ + +## アルベド語とはなんですか + +* see: https://www.jp.square-enix.com/column/detail/3/ + +## Requirement + +```console +opam install js_of_ocaml-ppx +``` + +## How to try this + +```console +./compile.sh +open index.html +``` diff --git a/bin/dune b/bin/dune new file mode 100644 index 0000000..da4d700 --- /dev/null +++ b/bin/dune @@ -0,0 +1,5 @@ +(executable + (name main) + (modes js) + (libraries uutf) + (preprocess (pps js_of_ocaml-ppx))) diff --git a/bin/main.ml b/bin/main.ml new file mode 100644 index 0000000..bfe2f7c --- /dev/null +++ b/bin/main.ml @@ -0,0 +1,46 @@ +open Js_of_ocaml +open Translate + +let get_params () : (string * string) list = Url.Current.arguments +let query key params = List.assoc_opt key params + +type operator = + Init | Translate of lang * string + +let operator_of_params params = + match (query "lang" params , query "ja" params, query "al" params) with + | (Some "al2ja", _, al) -> + let text = Option.value al ~default:"" in + Translate (Albhed_to_Ja, text) + | (Some "ja2al", ja, _) -> + let text = Option.value ja ~default:"" in + Translate (Ja_to_Albhed, text) + | _ -> Init + +let init () = + print_endline "init"; + (Dom_html.getElementById_exn "tweet")##.innerHTML := Js.string (Tweet_button.show Albhed_to_Ja "マギレヤキセ"); + () +let translate lang src = + print_endline "translate"; + let dst = Translate.f lang src in + (Dom_html.getElementById_exn "tweet")##.innerHTML := Js.string (Tweet_button.show lang src); + match lang with + | Albhed_to_Ja -> + (Dom_html.getElementById_exn "japanese")##.innerText := Js.string dst; + (Dom_html.getElementById_exn "albhed")##.innerText := Js.string src + | Ja_to_Albhed -> + (Dom_html.getElementById_exn "japanese")##.innerText := Js.string src; + (Dom_html.getElementById_exn "albhed")##.innerText := Js.string dst + +let onload _ = + let params = get_params () in + begin match operator_of_params params with + | Init -> init () + | Translate (lang, text) -> translate lang text + end; + Js._false + + +let () = + Dom_html.window##.onload := Dom_html.handler onload diff --git a/bin/translate.ml b/bin/translate.ml new file mode 100644 index 0000000..f8a6519 --- /dev/null +++ b/bin/translate.ml @@ -0,0 +1,41 @@ +type lang = Albhed_to_Ja | Ja_to_Albhed + +let table = [ (*c.f. https://www.jp.square-enix.com/column/detail/3/ *) + ("あ", "ワ"); ("い", "ミ"); ("う", "フ"); ("え", "ネ"); ("お", "ト"); + ("か", "ア"); ("き", "チ"); ("く", "ル"); ("け", "テ"); ("こ", "ヨ"); + ("さ", "ラ"); ("し", "キ"); ("す", "ヌ"); ("せ", "ヘ"); ("そ", "ホ"); + ("た", "サ"); ("ち", "ヒ"); ("つ", "ユ"); ("て", "セ"); ("と", "ソ"); + ("な", "ハ"); ("に", "シ"); ("ぬ", "ス"); ("ね", "メ"); ("の", "オ"); + ("は", "マ"); ("ひ", "リ"); ("ふ", "ク"); ("へ", "ケ"); ("ほ", "ロ"); + ("ま", "ヤ"); ("み", "イ"); ("む", "ツ"); ("め", "レ"); ("も", "コ"); + ("や", "タ"); ("ゆ", "ヲ"); ("よ", "モ"); + ("ら", "ナ"); ("り", "ニ"); ("る", "ウ"); ("れ", "エ"); ("ろ", "ノ"); + ("わ", "カ"); ("を", "ム"); + ("ん", "ン"); ("っ", "ッ"); + ("が", "ダ"); ("ぎ", "ヂ"); ("ぐ", "ヅ"); ("げ", "デ"); ("ご", "ゾ"); + ("ざ", "バ"); ("じ", "ギ"); ("ず", "ブ"); ("ぜ", "ゲ"); ("ぞ", "ボ"); + ("だ", "ガ"); ("ぢ", "ビ"); ("づ", "グ"); ("で", "ベ"); ("ど", "ゴ"); + ("ば", "ザ"); ("び", "ジ"); ("ぶ", "ズ"); ("べ", "ゼ"); ("ぼ", "ド"); + ("ぱ", "プ"); ("ぴ", "ポ"); ("ぷ", "ピ"); ("ぺ", "パ"); ("ぽ", "ペ"); + ] + +let albhed_to_ja uchar = + match List.find_opt (fun (_ja, al) -> Utf8.uchar_of_string al = uchar) table with + | None -> uchar + | Some (ja, _) -> Utf8.uchar_of_string ja + +let ja_to_albhed uchar = + match List.find_opt (fun (ja, _al) -> Utf8.uchar_of_string ja = uchar) table with + | None -> uchar + | Some (_, al) -> Utf8.uchar_of_string al + +let f lang text = + match lang with + | Albhed_to_Ja -> + Utf8.of_string text + |> Utf8.map albhed_to_ja + |> Utf8.to_string + | Ja_to_Albhed -> + Utf8.of_string text + |> Utf8.map ja_to_albhed + |> Utf8.to_string diff --git a/bin/tweet_button.ml b/bin/tweet_button.ml new file mode 100644 index 0000000..cf0edec --- /dev/null +++ b/bin/tweet_button.ml @@ -0,0 +1,26 @@ +open Js_of_ocaml + +let button_text = "アルベド語をツイートする" + +let hashtag = Url.urlencode "アルベド語翻訳" + +let body text = + Printf.sprintf {|%s + +翻訳をみる → |} text + |> Url.urlencode + +let show lang text = + Printf.printf "Tweet_button.show: '%s'" text; + let link = + let query = + match lang with + | Translate.Albhed_to_Ja -> Url.encode_arguments [("lang", "al2ja"); ("al", text)] + | Ja_to_Albhed -> Url.encode_arguments [("lang", "ja2al"); ("ja", text)] + in + let base = "proof-ninja.github.io/albhed" in + "https://" ^ base ^ "?" ^ query + in + Printf.sprintf {|%s|} + hashtag + (body text) (Url.urlencode link) button_text diff --git a/bin/utf8.ml b/bin/utf8.ml new file mode 100644 index 0000000..21558a2 --- /dev/null +++ b/bin/utf8.ml @@ -0,0 +1,25 @@ +type t = string + +let fold f init ustr = + Uutf.String.fold_utf_8 (fun acc _i -> function + | `Uchar uchar -> f acc uchar + | `Malformed _s -> acc) init ustr + +let map f ustr = + let buf = Buffer.create (String.length ustr) in + fold (fun buf uchar -> Uutf.Buffer.add_utf_8 buf (f uchar); buf) buf ustr + |> Buffer.contents + +let uchar_of_string s = + let decoder = Uutf.decoder ~encoding:`UTF_8 (`String s) in + match Uutf.decode decoder with + | `Uchar u -> u + | _ -> failwith "Invalid UTF-8 character" + +let string_of_uchar uchar = + let buf = Buffer.create 4 in + Uutf.Buffer.add_utf_8 buf uchar; + Buffer.contents buf + +let of_string s = s +let to_string s = s diff --git a/bin/utf8.mli b/bin/utf8.mli new file mode 100644 index 0000000..5a70ee0 --- /dev/null +++ b/bin/utf8.mli @@ -0,0 +1,10 @@ +type t + +val fold : ('a -> Uchar.t -> 'a) -> 'a -> t -> 'a +val map : (Uchar.t -> Uchar.t) -> t -> t + +val uchar_of_string : string -> Uchar.t +val string_of_uchar : Uchar.t -> string + +val of_string : string -> t +val to_string : t -> string diff --git a/compile.sh b/compile.sh new file mode 100755 index 0000000..fb7c882 --- /dev/null +++ b/compile.sh @@ -0,0 +1,2 @@ +set -eux +dune build bin/main.bc.js diff --git a/dune b/dune new file mode 100644 index 0000000..ef15733 --- /dev/null +++ b/dune @@ -0,0 +1,7 @@ +(alias + (name default) + (deps + bin/main.bc.js + index.html + (glob_files statics/*)) +) diff --git a/dune-project b/dune-project new file mode 100644 index 0000000..6ab29a2 --- /dev/null +++ b/dune-project @@ -0,0 +1,26 @@ +(lang dune 3.0) + +(name jsoo-alhbed) + +(generate_opam_files true) + +(source + (github username/reponame)) + +(authors "Yoshihiro Imai") + +(maintainers "Yoshihiro Imai") + +(license LICENSE) + +(documentation https://url/to/documentation) + +(package + (name jsoo-alhbed) + (synopsis "A short synopsis") + (description "A longer description") + (depends ocaml dune js_of_ocaml-ppx uutf) + (tags + (topics "to describe" your project))) + +; See the complete stanza docs at https://dune.readthedocs.io/en/stable/reference/dune-project/index.html diff --git a/index.html b/index.html new file mode 100644 index 0000000..690821a --- /dev/null +++ b/index.html @@ -0,0 +1,29 @@ + + + + + + +
+
+
+

アルベド語 翻訳

+
+
+
+ + + + +
+ +
+ +
Message Sent
+ + +
+
+
+ + diff --git a/statics/style.css b/statics/style.css new file mode 100644 index 0000000..c51d780 --- /dev/null +++ b/statics/style.css @@ -0,0 +1,143 @@ + + +body { + padding-top:25px; + background-color:#454545; + margin-left:10px; + margin-right:10px; +} +.container { + max-width:600px; + margin:0 auto; + text-align:center; + -webkit-border-radius:6px; + -moz-border-radius:6px; + border-radius:6px; + background-color:#FAFAFA; +} +.head { + -webkit-border-radius:6px 6px 0px 0px; + -moz-border-radius:6px 6px 0px 0px; + border-radius:6px 6px 0px 0px; + background-color:#2ABCA7; + color:#FAFAFA; +} +h2 { + text-align:center; + padding:18px 0 18px 0; + font-size: 1.4em; +} +label { + text-align:left; +} +input { + margin-bottom:10px; +} +textarea { + height:100px; + margin-bottom:10px; +} +input:first-of-type +{ + margin-top:35px; +} +input[type="radio"] { + width: 10%; +} +input, textarea { + font-size: 1em; + padding: 15px 10px 10px; + font-family: 'Source Sans Pro',arial,sans-serif; + border: 1px solid #cecece; + background: #d7d7d7; + // color:#FAFAFA; + color: #646d76 + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; + -moz-background-clip: padding; + -webkit-background-clip: padding-box; + background-clip: padding-box; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + width: 80%; + max-width: 600px; +} +::-webkit-input-placeholder { + color: #FAFAFA; +} +:-moz-placeholder { + color: #FAFAFA; +} +::-moz-placeholder { + color: #FAFAFA; +} +:-ms-input-placeholder { + color: #FAFAFA; +} +button { + margin-top:15px; + margin-bottom:25px; + background-color:#2ABCA7; + padding: 12px 45px; + -ms-border-radius: 5px; + -o-border-radius: 5px; + border-radius: 5px; + border: 1px solid #2ABCA7; + -webkit-transition: .5s; + transition: .5s; + display: inline-block; + cursor: pointer; + width:30%; + color:#fff; +} +button:hover, .button:hover { + background:#19a08c; +} +label.error { + font-family:'Source Sans Pro',arial,sans-serif; + font-size:1em; + display:block; + padding-top:10px; + padding-bottom:10px; + background-color:#d89c9c; + width: 80%; + margin:auto; + color: #FAFAFA; + -webkit-border-radius:6px; + -moz-border-radius:6px; + border-radius:6px; +} +/* media queries */ +@media (max-width: 700px) { + label.error { + width: 90%; + } + input[type="radio"]{ + width: 40%; + } + input[type="submit"], textarea { + width: 90%; + } + button { + width:90%; + } + body { + padding-top:10px; + } +} +.message { + font-family:'Source Sans Pro',arial,sans-serif; + font-size:1.1em; + display:none; + padding-top:10px; + padding-bottom:10px; + background-color:#2ABCA7; + width: 80%; + margin:auto; + color: #FAFAFA; + -webkit-border-radius:6px; + -moz-border-radius:6px; + border-radius:6px; +}