Currently, MW core requires a talk namespace to be defined for every subject namespace. However, in some cases, having associated talk pages is unnecessary or even unwanted, for instance for Flow's topic namespace. Extensions like flow try to work around this by not defining the talk namespace, and trying to hack around the fallout this causes in core code. Other extensions may define the unwanted namespace, but hide links and prevent editing. Both should not be necessary.
The assumption of having a talk namespace associated is manifest in the MWNamespace::getTalk() and Title::getTalkNsText() methods. More importantly, the assumption is spread throughout the code base by calls to Title::getTalkPage(), which is guaranteed to always return a Title. The requirement is also documented in https://www.mediawiki.org/wiki/Manual:Using_custom_namespaces#Creating_a_custom_namespace.
The behavior of Title::getTalkPage() is scary in the case no talk namespace is defined: The Title object returned by Title::getTalkPage() will eventually rely on Title::prefix() to add the namespace prefix. Title::prefix() will take the return value of Title::getNsText() unseen, and prepend it to the title text. However, Title::getNsText() will return false if the namespace is undefined! So the resulting title has an empty namespace prefix, effectively causing it to refer to the main namespace, by virtue of the fact that turning the boolean false into a string in PHP results in an empty string.
This behavior causes wrong links to be shown in the "talk" tab on top of the page, but surprisingly few other places, even though Title::getTalkPage is called a lot by core, and various extensions. Most such calls are for user talk pages. Some classes that use getTalkPage for all kinds of pages are SpecialEditWatchlist and code that constructs FeedItems. The $comments parameter of a FeedItem is typically set to the URL of the page's talk page. However, FeedItem comments are currently not used in Autom output (the respective line in AtomFeed::outItem is commented out), and RSS feeds are disabled on the live site.
So it seems the fallout of ignoring the requirement of defining a talk namespace is currently limited, but only by sheer luck. It would be much better to drop the requirement from core.
UPDATED after discussion on IRC on June 7 2017.
Change the behavior of relevant methods in Title as follows:
- MWNamespace::canTalk() should check whether the talk NS is actually defined. This would also change the behavior of Title::canTalk().
- Title::canTalk is a strange name. Perhaps introduce Title::hasTalkPage to replace it.
- Title::getTalkPage() should throw an exception if Title::hasTalkPage returns false.
- Callers should check canTalk() first, unless the title is known to be on in a well known namespace with a guaranteed talk namespace associated, as is the case when calling getTalkPage on a user page title, for example.
- Title::getTalkNSText() should declare that it may return false. This is already the case, but not documented.
- Title::prefix() should check whether getNsText() returned false. In that case, it should prefix the title with Special:BadTitle/NS<number>:<title>. This behavior will be inherited by getPrefixedText() and friends.
- Note that using Special:BadTitle here doesn't allow for a successful round trip. But neither did the old behavior, which caused the text form to refer to the main namespace.
- Title::makeTitle must continue to return Title objects when called with a bad namespace ID, so we can still render e.g. log entries about pages in namespaces that were removed.
- We could introduce a sensitive version, Title::makeTitleThrow, but Title::makeTitleSafe can already be used for this. Title::makeTitleThrow could behave just like Title::makeTitleSafe, except for throwing an exception instead of returning null for bad namespaces and malformed titles.
- Introduce Title::isValid(), that applies the same checks as makeTitleSafe.
- Title::canExist() should probably check Title::isValid().
All code that calls Title::getTalkPage() must be changed to check Title::canTalk() first.