logo科技微讯

精确计算微信公众号模板消息字数限制

作者:科技微讯
日期:2023-03-24
📝 笔记

微信发布公告表示,从 2023-03-30 开始,微信公众号模板消息不再展示备注、不再支持自定义颜色、不再显示表情符号。

2023-03-20 更新:微信再发公告,把新政策开始执行的时间从 2023-03-30 改为 2023-05-04,并且新增不再显示首行内容的规则。

2023-05-04 更新:微信第三次发公告,新增模板消息内的每一行内容字数不能超过 20 的限制。

问题描述

公众号开发文档显示,模板消息的内容不能超过 200 字符:

模板内容长度不能超过 200 个字符,且必须有至少 10 个固定文字或标点。

在实际的开发中,如果发送的模板消息字符数超过 200,模板消息依然可以正常发送,只是超过的那部分会显示为省略号,因为“备注”显示在最后,所以自动省略不是一个问题:

template_1

但在新的规则下,“备注”不再显示,所以我们需要把信息挪到“服务时间”之前的位置,比如“服务状态”,此时超字数会导致服务时间被自动省略,这是一个问题:

template_2

为了解决这个问题,需要精确计算“服务状态”的字数,如果超字数,在发送模板消息之前就把超出的部分修改为省略号,如下:

template_3

后来模板消息又增加了 20 个字的限制,于是服务状态可以展示的字数大幅减少,下图的省略号是微信自动添加:

template_20_limit

有趣的是,如果是连续的英文数字,虽然多了 20 个字的限制,但可显示的字数似乎并没有减少,如下所示,“服务状态”可以一直显示,甚至挤占了“服务时间”的显示空间:

template_4

现在的问题是,这 200 个字、20 个字是怎么计算的?

20 个字的计算方法

我还不知道 20 个字是按什么标准计算的,但这 20 个字的字,和 200 个字的字,计算方法有很大不同。

简单观察了一下,20 个字的计算有以下规律:

  • 所有字符加起来不超过 200 字依然成立;
  • 显示的 20 个字不包括自动添加的省略号;
  • 空格、中英文标点符号算一个字;
  • 一个汉字、连续数字、连续英文单词、连续的数字和英文组合都算一个字,例如“hello123”算一个字,“我们”算两个字;

连续的数字、英文字母被视为一个字,所以上图的“服务状态”字数其实只有 1,小于 20 个字的限制,按道理可以完全显示出来。但同时,因为所有项目的字符加起来超过了 200 字,所以它并不能显示完整,还侵占了“服务时间”的显示。

那 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;

以上两个字数的计算方法不一样,20 个字的限制会把英文单词、连续的数字等看成了一个字,而 200 个字的限制则会把单个数字、单个字母(不是单词)都看成了一个字。

因为超过 200 可能会导致最后一个字段完全显示省略号,所以建议对第二条规则进行处理,例如确保服务时间、服务类型都显示完整,然后计算出服务状态最多可以显示多少个字符,如果字数超了就在代码中把多出的用省略号替代。

做了以上这一步之后,在通知中心的使用情景下,我可以不对服务状态的字数是否超 20 字进行判断,因为我不知道这里的 20 字的具体明确的计算方法,而且微信会自动把多出的字符替换为省略号,不需要担心它会挤压其他信息。

donation赞赏
thumbsup0
thumbsdown0
暂无评论