2023-03-24:微信发布公告表示,从 2023-03-30 开始,微信公众号模板消息不再展示备注、不再支持自定义颜色、不再显示表情符号。
2023-03-30:微信再发公告,把新政策开始执行的时间从 2023-03-30 改为 2023-05-04,并且新增不再显示首行内容的规则。
2023-05-04:微信第三次发公告,新增模板消息内的每一行内容字数不能超过 20 的限制。
温馨提示:这篇文章是关于公众号模板消息的 20 个字计算方法,小程序订阅消息的 20 个字计算方法和公众号的不一样。一个非常重要的区别是:公众号模板消息超字数不会报错,可以正常发送,只是会自动省略超字数部分;而小程序订阅消息超字数会直接报错。另外,小程序订阅消息的每一个字母、数字、标点都占用一个字,而公众号模板消息连续的数字或字母会算作一个字,下面有详细说明。
公众号开发文档显示,模板消息的内容不能超过 200 字符:
模板内容长度不能超过 200 个字符,且必须有至少 10 个固定文字或标点。
在实际的开发中,如果发送的模板消息字符数超过 200,模板消息依然可以正常发送,只是超过的那部分会显示为省略号,因为“备注”显示在最后,所以自动省略不是一个问题:
但在新的规则下,“备注”不再显示,所以我们需要把信息挪到“服务时间”之前的位置,比如“服务状态”,此时超字数会导致服务时间被自动省略,这是一个问题:
为了解决这个问题,需要精确计算“服务状态”的字数,如果超字数,在发送模板消息之前就把超出的部分修改为省略号,如下:
后来模板消息又增加了 20 个字的限制,于是服务状态可以展示的字数大幅减少,下图的省略号是微信自动添加:
有趣的是,如果是连续的英文数字,虽然多了 20 个字的限制,但可显示的字数似乎并没有减少,如下所示,“服务状态”可以一直显示,甚至挤占了“服务时间”的显示空间:
现在的问题是,这 200 个字、20 个字是怎么计算的?
我还不知道 20 个字是按什么标准计算的,但这 20 个字的字,和 200 个字的字,计算方法有很大不同。
简单观察了一下,20 个字的计算有以下规律:
连续的数字、英文字母被视为一个字,所以上图的“服务状态”字数其实只有 1,小于 20 个字的限制,按道理可以完全显示出来。但同时,因为所有项目的字符加起来超过了 200 字,所以它并不能显示完整,还侵占了“服务时间”的显示。
那 200 个字的限制又是怎么计算的呢?
200 个字的计算方法如下:
const string = "Hello 🌎!";
//[...string] 会变成:[ 'H', 'e', 'l', 'l', 'o', ' ', '🌎', '!' ]
const wordCount = [...string].length; //8
注意是 [...string].length
而不是 string.length
,因为 🌎 的字数是 1,但它的 length
是 2:
const string = "Hello 🌎!";
const length = string.length; //9
顺便提一下,string.length
是计算一段字符以 UTF-16 编码的长度,如果要计算其 UTF-8 的长度(即字节数),则是:
const string = "Hello 🌎!";
//node.js
const length = Buffer.byteLength(string); //11
//browser、node.js
const _length = new TextEncoder().encode(string).length; //11
ChatGPT 告诉我,一个 ASCII 字符就是一个字节,一个 Latin-1 Supplement 字符是 2 个字节,其他 BMP 字符是 3 个字节,非 BMP 字符都是 4 个字节:
1 byte for ASCII, 2 bytes for Latin-1 Supplement, 3 bytes for other BMP characters, 4 bytes for non-BMP characters.
如果要从一段字符中,获取前 N 个字:
const N = 7;
const string = "Hello 🌎!";
const str = [...string].slice(0, N).join(""); //Hello 🌎
注意不能使用 string.slice()
,因为它也是基于 UTF-16 进行 slice
的,正如前面所说,🌎 由两个 UTF-16 编码组成,如果只 slice
了其中一个,会出现乱码:
const N = 7;
const string = "Hello 🌎!";
const str = string.slice(0, N); //Hello �
理解了以上内容,我们就可以计算在不自动省略的情况下,“服务状态”的字数是多少,并把一段过长的文字裁切成刚刚可以放进模板消息的字符串:
const first = "本通知由您主动订阅,如无需通知,请取消订阅";
const keyword1 = "测试测试";
const keyword3 = "2023-03-24 19:00:00";
const totalLengthAllowed = 200;
const currentLength = [...(first + keyword1 + keyword3)].length;
const keyword2LengthAllowed = totalLengthAllowed - currentLength;
const longString = Array.from({ length: 100 })
.fill(`科技微讯😃通知中心`)
.join("");
const keyword2 =
[...longString].length > keyword2LengthAllowed
? [...longString].slice(0, keyword2LengthAllowed - 3).join("") + "..."
: longString;
console.log(keyword2); //keyword2 刚好可以避免自动省略
公众号模板消息有 2 条字数限制措施:
以上两个字数的计算方法不一样,20 个字的限制会把英文单词、连续的数字等看成了一个字,而 200 个字的限制则会把单个数字、单个字母(不是单词)都看成了一个字。
因为超过 200 可能会导致最后一个字段完全显示省略号,所以建议对第二条规则进行处理,例如确保服务时间、服务类型都显示完整,然后计算出服务状态最多可以显示多少个字符,如果字数超了就在代码中把多出的用省略号替代。
做了以上这一步之后,在通知中心的使用情景下,我可以不对服务状态的字数是否超 20 字进行判断,因为我不知道这里的 20 字的具体明确的计算方法,而且微信会自动把多出的字符替换为省略号,不需要担心它会挤压其他信息。