SuperAPI AI 工具集

string-resource-format-limits

编程与开发

查看项目

更新于 10-17 16:17

技能介绍

用途

本文围绕 Kotlin 多平台(Compose Multiplatform)字符串资源,说明资源格式化的能力边界,以及由此产生的常见缺陷。核心结论是:资源层只负责把已经格式化好的片段拼接起来,不负责格式化本身。

主要能力与限制

资源查找只理解 %1$s 和 %1$d 这类纯位置占位符。它不支持格式说明符中的标志与宽度部分,没有字面百分号的转义方式,也不会做取整、截断或补位。如果在资源里写 %1$02d:00,这个说明符会作为文本直接显示在屏幕上。

因此分工是固定的:每个值都在代码里处理完,资源只负责拼接。例如小时标签补零要在 Kotlin 中用 padStart 完成,资源本身写 "%1$s – %2$s";百分比因为 % 号无法在资源中保留,要在 Kotlin 里拼成 "$coverage%" 再作为字符串传入。

常见陷阱

  • 失败是静默的,并且看起来像显示问题:未识别的说明符会原样渲染,容易被当成模板故障而不是格式化错误;只有执行到使用该说明符的分支时才会暴露。
  • 翻译容易破坏占位符,而且通常只在某一个语言中出问题:文中扫描 26 个翻译,只发现一个文件丢失了占位符末尾的类型字符,留下裸的 %1$ 加空格。基准语言正常,评审也都在基准语言进行,但该语言会把说明符原样渲染出来。
  • 用存储单位直接显示数字不是小的格式问题:以秒为单位的总时长显示为 "$seconds seconds" 时,屏幕上会出现一个没人能一眼换算成“十三小时多”的五位数。应在边界处换算,并在单位不再携带信息时去掉它。
  • 硬编码字面量是同一类遗漏的早期形态:Text("Listened time") 这类字符串不会进入任何导出文件,翻译者看不到,读取资源文件的检查也发现不了。
  • 日期时间库中的英文月份名是常量而不是本地化查找:多平台库只提供 MonthNames.ENGLISH_FULL 和 ENGLISH_ABBREVIATED,没有本地化替代,因此用它们格式化的日期在任何语言下都显示英文。解决办法是把十二个月份名做成字符串资源,用 when 选择。
  • 该 when 需要 else;如果月份类型在共享代码中不是穷尽枚举,编译器会要求回退分支。直接返回一月会让意外值渲染成看似合理的日期,应选择读者能识别为错误的回退,或直接报错。
  • 修好一个界面不等于落实了规则:同一代码树中其他三处调用点仍在使用英文常量格式化日期,直到有人用 grep 搜索才会暴露。
  • 把查找包成 @Composable fun formatX() 后,所有格式化器都只能在组合中使用:无法脱离组合做单元测试,也无法从视图模型、通知或小组件调用。这对纯显示辅助函数通常是合适的取舍,但对后台路径也需要的能力则不合适。

验证方式

扫描所有语言,而不仅是基准语言:

find . -path 'composeResources/values' -name 'strings.xml' -not -path '/build/'
-exec grep -noE '%[0-9]+$[^sd<]|%%' {} +

预期没有输出;任何命中要么是会被原样渲染的标志/宽度说明符,要么是被翻译截断的占位符。文中在 26 个语言中扫描,只出现一例。

查找硬编码语言的日期格式化:

grep -rn --include='*.kt' "MonthNames.ENGLISH" . | grep -v '/build/'

文中出现五处命中,其中两处是记录修复的注释,三处是仍在非英文设备上输出英文月份名的线上调用点。

查找从未进入翻译流程的用户可见字面量:

grep -rn --include='.kt' -E 'Text(\s"[A-Z]' . | grep -v '/build/'

该模式只匹配直接作为第一个参数传入的字面量,不覆盖先构建到变量或被插值的情况,所以命中数应视为下限。

最后把设备语言切换成你读不懂的语言,逐屏走查。任何你仍然能看懂的内容,要么是硬编码字面量,要么是常量支撑的日期。这一项能同时覆盖上述三种形态,也是唯一不需要 grep 就能判断正确的检查。

适用场景

适用于使用 Compose Multiplatform 资源系统的 Kotlin 多平台项目,尤其是需要多语言支持、处理日期时间与数值显示、并希望把格式化职责收敛到代码层的团队。