Sorry for the video essay title
When you TextChannel:SendAsync() a user message, one of the TextChatMessageStatus Enums that can be returned to you is Enum.TextChatMessageStatus.MessageTooLong. The conditions by which this enum trips are not documented (plz), but it appears to occur with any string:len() > 200.
This leads to an unavoidable UX problem if you want your players to be able to chat in any language up to a 200 character limit. Because it (seemingly) does a raw length check, players who chat in languages that, for example, use Cyrillic characters, have a smaller maximum message size available to them.
This is also not very intuitive. In fact, the default TextChatService Roblox provides out-of-the-box can be used to demonstrate this - because the ChatBar on the unconfigured TextChatService is seemingly doing a codepoint check, but TextChatMessages look at the raw length, english users can never type out a message that exceeds the maximum length:
Below: A gif where I paste in a 200 character message using only english characters and send it. Then, I paste the same message in, delete two characters, and spam my 8 key. The UX is nice here because I can’t keep typing past 200 characters.
Here is the same example but with a paste largely composed of Cyrillic characters. This message is slightly under 100 characters if you utf8:len(msg) but if you take msg:len() it’s slightly under 200, so it sends fine still. However, when I paste the message again and spam some extra characters on the end, the ChatBar allows me to enter them and then rejects the message for length when I press enter.
Assumedly, this is occuring because the ChatBar capping does something akin to this when a user changes the TextBox by entering a new character:
if utf8.len(utf8.nfcnormalize(TextBox.Text)) > 200 then
--Set the TextBox.Text to the previous string so the user knows they've typed up to the message length limit.
else
--Not at message length limit, store TextBox.Text in the previous string var
end
The above code is, intuitively, the desired way to do something like this, and also how the engineers who worked on the TCS ChatBar (+ probably lots of devs too) seemingly expected chat to behave. However, because TextChatService internally computes its maximum 200 length using a check that doesn’t account for this, it means, as an example, players typing in Cyrillic have less they can put into a message vs English speakers.
if TextBox.Text:len() > 200 then
--What you seemingly need to do to prevent the issue demonstrated above with Cyrillic - intuitively you'd assume utf8.len() would be the correct way to go about checking maximum length, though (and it seems the default ChatBar implementation concurs with this, since it demonstrates the funky behavior).
else
--Not at message length limit, store TextBox.Text in the previous string var
end
Below: Example of the same Cyrillic message / trying to add more onto it, but with the :len() check instead of utf8.len() (using a forked legacy chat with TCS implemented so we can edit the way the ChatBar behaves) This prevents the UX of player messages being nuked, which is good, but players using Cyrillic characters have their message size restricted more than English speakers still, due to the innate limit imposed by TCS when you :SendAsync().
Expected behavior
Ideally, the condition at which TextChatMessageStatus.MessageTooLong occurs should be documented clearly, so devs who make their own ChatBar know to cap a player input by doing an operation like :len() as opposed to utf8.len(). This issue popped up to us because Russian players of our game were typing large dialogues to other players, just to send them and have it delete the entire message and tell the player it was too long. This understandably frustrated the players, and if you use a non-customized ChatBar/use the Out-Of-The-Box Roblox TCS you can’t address this concern, you’d have to
Intuitively / as a developer, you’d expect all languages to be treated equally when it comes to maximum message lengths. It would be great if TextChatService decided if a message was too long via checking codepoint length / some length op that doesn’t change the UX based on what language you are using.