Repository navigation
Deprecate the MECAB magic word in LgRegexpWordCharacters #288
Description
LgRegexpWordCharacters = 'MECAB' is a magic string: the field that should hold a word-characters regex instead holds a literal marker meaning "tokenize with MeCab". LgParserType now exists to say that properly, so the magic word should go — but it is not a one-line swap, because it carries a second fact that LgParserType does not.
It means two different things
Ten PHP sites read it, and they do not agree on what it is.
As a parser selector — genuinely superseded by LgParserType:
Language::getEffectiveParserType()(Language.php:434)ParserRegistry::resolveParserType()/resolveParserTypeFromRow()(ParserRegistry.php:199,228)ExternalParser.php:551
As "this language has no spaces between words" — not superseded, and this is the blocker. In SentenceService it appears in six places alongside LgSplitEachChar and LgRemoveSpaces, governing how spaces are reconstructed around tokens:
// SentenceService.php:333, 362 (and 136, 290, 455, 548)
if (!$removeSpaces && !($splitEachChar || 'MECAB' == strtoupper(trim($termchar)))) {Setting LgParserType = 'mecab' and clearing the field would silently change Japanese spacing, because nothing else tells SentenceService the language is space-free. That fact needs an explicit home first — LgRemoveSpaces already exists, or it could be derived from the parser's own capabilities.
It also consumes the field it occupies: a MeCab language has no word-characters regex at all, which interacts badly with anything reasoning about word characters (see #278).
It is still a live input path
Not a legacy artifact to read-only-support. The word-characters selector still offers "MeCab (recommended)" (locale/en/language.json → form.regexp_alt_mecab), and language_form.ts::wordCharChange() writes the literal string into the field. New magic-word languages are still being created today.
Meanwhile the modern path has already moved on without it: langdefs.json's Japanese preset declares "parserType": "mecab", and #281 is what makes that preset take effect.
A bug it has already caused
Every reader normalizes before comparing — strtoupper(trim($x)) === 'MECAB' — except one:
// GetPhoneticReading.php:54
if ($wordCharacters !== "mecab") {
return $text;
}A language whose field holds MECAB in any other casing (an old install, an import, a hand-edit) silently gets no phonetic reading: no error, just the input back. Worth fixing on its own, independently of this issue.
Why it matters beyond tidiness
Parser identity currently lives in three places — the magic word, LgSplitEachChar, and LgParserType — and 20251223_120000_add_parser_type.sql backfills the third from the first two. That ambiguity is what made #281 change tokenization for every CJK language on an upgraded install: it started trusting a column holding derived values. The fix there (17262f5) reads a type that merely restates its neighbouring flag as the flag rather than as intent — correct for now, and a workaround for this issue. Retiring the magic word is what would let LgParserType simply mean what the user chose, and let that workaround be deleted.
Suggested plan
- Give "no spaces between words" an explicit home, so
SentenceServicestops asking about the magic word. - Migration: for
UPPER(TRIM(LgRegexpWordCharacters)) = 'MECAB', setLgParserType = 'mecab'(the backfill already did), set the space flag, and put a real regex (or empty) back in the word-characters field. - Drop the MeCab option from the word-characters selector, so nothing new is written; the parser dropdown already covers it.
- Keep the readers accepting the magic word for one release, logging a deprecation.
- Next release: delete the readers, and with them the
restatesALegacySignal()workaround inParserRegistry.
Roughly 10 PHP sites, 2 TS files, 1 migration, 1 locale key. Small, but it changes how Japanese renders, so it wants a before/after parse comparison across every language — the same one that caught the #281 regression.
Activity
refactor(language): give the MeCab magic word one reader
feat(language): retire the MECAB magic word from the database
Merge pull request #305 from HugoFara/develop
feat(language): deprecate the MECAB magic word out loud
chore(release): 3.7.0-fork
HugoFara commentedon Sep 4, 2026
Status against the plan above: steps 1–4 shipped in 3.7.0. Step 5 is a 3.8.0 item, and deliberately so.
WordSpacing(a57b5f2) —SentenceService's six hand-written comparisons now callseparatesWordsWithSpaces()LgParserType/LgRemoveSpaces, restore a real regex20260831_120000_retire_mecab_magic_word.sql(df49b12)form.regexp_alt_mecabandlanguage_form.ts::wordCharChange()both gone (a57b5f2)WordSpacing::noteDeprecatedUse()→error_log, deduplicated per request, plus the admin server data panel (c357e32)restatesALegacySignal()workaroundThe casing bug called out above is fixed too (a57b5f2):
GetPhoneticReadingcompared against lowercase"mecab"without normalising, so a field holdingMECABin any other casing silently returned the input unchanged. It resolves throughgetEffectiveParserType() !== ParserSelection::MECABnow.Why step 5 waits rather than lands now
Step 4 exists to give installs one release to migrate, and the notice it logs says so in as many words — "Support for the marker is removed in a future release (see issue #288)". Deleting the readers in the same release that started warning would make that promise false for anyone who upgrades across both at once. So this stays open until 3.8.0 rather than being closed as substantially done.
What 3.8.0 deletes
WordSpacing::usesMecabMagicWord()and theMECAB_MAGIC_WORDconstantLanguage::getEffectiveParserType(),ParserRegistry::resolveParserType()/resolveParserTypeFromRow(), andExternalParser.php:552ParserRegistry::restatesALegacySignal()— the fix(language): make the parser setting real, and Chinese use jieba (#278) #281 workaround. This is the prize: it exists becauseLgParserTypecannot yet be trusted to mean what the user chose rather than a restatement of a derived neighbouring flag. Retiring the marker is what lets that column simply mean intent.MySqlLanguageRepository::findUsingDeprecatedMecabMarker()and theMaintenance.phpquerydeprecated_mecab_languagespanel in server data, with its two locale keysTwo cautions when picking it up
Order matters at the end. The server-data panel is what tells an admin whether their install still has affected rows, so it is the last thing removed, not the first — pulling it early removes the evidence that the readers are still needed.
It changes how Japanese renders. Per the closing note above, this wants the before/after parse comparison across every language — the same one that caught the #281 regression — not just a green test suite.
No GitHub milestone set: this repo only has the dormant "LWT 3" milestone and none of the current issues use per-version ones, so this note is the record rather than inventing a convention. Happy to create a 3.8.0 milestone if you'd rather track it that way.