187 lines
5.9 KiB
PHP
187 lines
5.9 KiB
PHP
<?php
|
|
|
|
namespace App\Controller;
|
|
|
|
use App\Entity\UnicodeNote;
|
|
use App\Service\AnkiService;
|
|
use App\Service\CharListService;
|
|
use App\Utils\Japanese;
|
|
use App\Utils\Number;
|
|
|
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
|
use Symfony\Component\HttpFoundation\Request;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
use Symfony\Component\HttpKernel\Attribute\MapQueryParameter;
|
|
use Symfony\Component\Routing\Attribute\Route;
|
|
use Symfony\Component\VarExporter\VarExporter;
|
|
|
|
#[Route('/kanji', name: 'app_kanji_')]
|
|
class KanjiController extends AbstractController
|
|
{
|
|
function __construct(
|
|
private AnkiService $anki,
|
|
private CharListService $charList,
|
|
private string $varBasepath,
|
|
) {}
|
|
|
|
public static function tmpl(string $path): string
|
|
{
|
|
return "kanji/$path.html.twig";
|
|
}
|
|
|
|
/** @return array<string, string> */
|
|
public function getIgnoredList(): array
|
|
{
|
|
$path = "{$this->getParameter('kernel.project_dir')}/data/ignored.list";
|
|
$ret = [];
|
|
|
|
$file = fopen($path, 'r');
|
|
while (($kanji = fgets($file)) !== false) {
|
|
$kanji = mb_trim($kanji);
|
|
$ret[$kanji] = '';
|
|
|
|
if (mb_strlen($kanji) !== 1) throw new \Exception(sprintf(
|
|
'Kanji expected, got "%s"',
|
|
$kanji,
|
|
));
|
|
while (($explanation = fgets($file)) !== false) {
|
|
if ($explanation === "\n") continue 2;
|
|
$ret[$kanji] .= $explanation;
|
|
}
|
|
$ret[$kanji] = mb_rtrim($ret[$kanji]);
|
|
}
|
|
|
|
return $ret;
|
|
}
|
|
|
|
/** @return array<string, array<string, 0>> */
|
|
private function getCharInfo(bool $force = false): array
|
|
{
|
|
$cacheFile = "$this->varBasepath/charlist.php";
|
|
|
|
if (!$force and file_exists($cacheFile)) return require $cacheFile;
|
|
|
|
$ret = [];
|
|
|
|
$lists = [
|
|
'sn' => $this->anki->getKnownSnKanjiCounts(),
|
|
'sln' => $this->anki->getKnownSlnKanjiCounts(),
|
|
'unicode' => $this->anki->getUnicodeKanji(),
|
|
'ignored' => $this->getIgnoredList(),
|
|
];
|
|
|
|
// @formatter:off
|
|
$storedLists = [
|
|
'taiwan', 'bushu', 'kyoyou', 'kyuujitai', 'kanken', 'hsk', 'zhcn',
|
|
'jimaku', 'ebook',
|
|
];
|
|
// @formatter:on
|
|
foreach ($storedLists as $storedName) {
|
|
$storedList = $this->charList->getList($storedName);
|
|
|
|
// Simple list
|
|
if (key_exists('chars', $storedList)) {
|
|
$lists[$storedName] = $storedList['chars'];
|
|
} else {
|
|
foreach ($storedList['sublists'] as $subname => $sublist) {
|
|
$lists["$storedName-$subname"] = $sublist['chars'];
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach ($lists as $listName => $list) {
|
|
foreach (array_keys($list) as $char) {
|
|
$ret[$char] ??= [];
|
|
$ret[$char][$listName] = 0;
|
|
}
|
|
}
|
|
|
|
file_put_contents($cacheFile, '<?php return ' . VarExporter::export($ret) . ';');
|
|
|
|
return $ret;
|
|
}
|
|
|
|
#[Route('/{start}-{end}', name: 'index')]
|
|
public function index(
|
|
string $start,
|
|
string $end,
|
|
Request $request,
|
|
): Response {
|
|
$charInfo = $this->getCharInfo(force: $request->isNoCache());
|
|
|
|
$completedRows = [];
|
|
$completedFlag = true;
|
|
$chars = [];
|
|
foreach (range(intval("{$start}0", 16), intval("{$end}f", 16)) as $codepoint) {
|
|
$charStr = mb_chr($codepoint, 'UTF-8');
|
|
|
|
if (
|
|
!key_exists('ignored', $charInfo[$charStr] ?? ['ignored' => 0])
|
|
and !key_exists('unicode', $charInfo[$charStr] ?? [])
|
|
) {
|
|
$completedFlag = false;
|
|
}
|
|
if (($codepoint + 1) % 0x10 === 0) {
|
|
if ($completedFlag === true) $completedRows[] = dechex($codepoint - 0xf);
|
|
$completedFlag = true;
|
|
}
|
|
|
|
$chars[] = [
|
|
'str' => $charStr,
|
|
'codepoint' => dechex($codepoint),
|
|
'lists' => $charInfo[$charStr] ?? [],
|
|
];
|
|
}
|
|
|
|
return $this->render(self::tmpl('grid'), [
|
|
'characters' => $chars,
|
|
'completed' => $completedRows,
|
|
]);
|
|
}
|
|
|
|
#[Route('/{codepoint}/register', 'register', methods: 'GET')]
|
|
public function register(string $codepoint): Response
|
|
{
|
|
// Properly check it's valid (make a util?)
|
|
$charStr = mb_chr(Number::hexint($codepoint), 'UTF-8');
|
|
|
|
$note = UnicodeNote::fromCharacter($charStr);
|
|
$terms = $this->anki->searchTerms($charStr);
|
|
$note->setTerms($terms);
|
|
|
|
$this->anki->addNote($note, UnicodeNote::DECK);
|
|
|
|
return new Response();
|
|
}
|
|
|
|
#[Route('view', 'view', methods: 'GET')]
|
|
public function view(#[MapQueryParameter] string $char): Response
|
|
{
|
|
$charStr = ctype_xdigit($char) ? Number::parseCodepoint($char) : $char;
|
|
$codepoint = ctype_xdigit($char) ? $char : dechex(mb_ord($char));
|
|
|
|
$charInfo = $this->getCharInfo()[$charStr] ?? [];
|
|
//$listTitles = $this->charList->getTitles();
|
|
|
|
$jiten = json_decode(file_get_contents(
|
|
"{$this->getParameter('kernel.project_dir')}/data/kanken-links.json",
|
|
), true);
|
|
|
|
$terms = $this->anki->searchTerms($charStr);
|
|
|
|
$ebookRef = require "$this->varBasepath/ebook-ref.php";
|
|
|
|
return $this->render(self::tmpl('view'), [
|
|
'ignored_text' => $this->getIgnoredList()[$charStr] ?? null,
|
|
'char' => $charStr,
|
|
'codepoint' => $codepoint,
|
|
'info' => $charInfo,
|
|
'terms' => $terms,
|
|
'ref' => $ebookRef[$charStr] ?? [],
|
|
'jiten_href' => key_exists($charStr, $jiten)
|
|
? "https://kanji.jitenon.jp/kanji{$jiten[$charStr]}"
|
|
: null
|
|
]);
|
|
}
|
|
}
|