Firebase Cloud Messaging(FCM)推送通知本地化实现技术问询
实现FCM推送通知本地化(待办状态变更场景)
刚好做过类似的FCM本地化推送需求,针对你提到的待办事项状态变更场景,我来分享一套成熟的实现方案——核心是把本地化逻辑交给客户端处理,服务端只传递关键参数,这样既灵活又能完美适配用户的系统语言,还不用服务端维护一堆多语言文案。
核心思路
不要让服务端生成多语言的通知文本,而是让服务端发送「通知类型标识+动态参数」,客户端收到后,根据自身的本地化字符串资源,把参数填充到对应的模板里,最后展示给用户。这样不管用户设备是什么语言,都能自动匹配对应的翻译。
具体实现步骤
1. 客户端准备本地化字符串资源
以Android为例,我们需要在不同语言的资源文件夹里定义带占位符的模板字符串,以及状态枚举的本地化文本:
- 英文模板(
res/values/strings.xml):
<string name="todo_status_changed">Status of %1$s changed to %2$s.</string> <string name="status_open">OPEN</string> <string name="status_in_progress">IN PROGRESS</string> <string name="status_done">DONE</string> <string name="unknown_todo">Unknown Todo</string> <string name="todo_update_title">Todo Update</string>
- 中文模板(
res/values-zh/strings.xml):
<string name="todo_status_changed">%1$s的状态已变更为%2$s。</string> <string name="status_open">未开始</string> <string name="status_in_progress">进行中</string> <string name="status_done">已完成</string> <string name="unknown_todo">未知待办</string> <string name="todo_update_title">待办更新</string>
iOS端的话,对应在Localizable.strings里做同样的配置即可,语法类似:
"todo_status_changed" = "%1$@的状态已变更为%2$@。"; "status_open" = "未开始"; "unknown_todo" = "未知待办";
2. 服务端构造FCM消息
服务端不需要处理任何本地化,只需要发送data类型的FCM消息(注意不能用notification类型,因为会被系统直接展示,客户端没机会处理),包含以下关键参数:
notification_type:标识通知类型(比如todo_status_change),方便客户端区分不同的通知场景todo_title:待办事项的标题(比如「吸尘」)todo_status:待办的状态枚举值(比如OPEN/IN_PROGRESS/DONE)
示例JSON payload:
{ "to": "用户的设备FCM令牌", "data": { "notification_type": "todo_status_change", "todo_title": "吸尘", "todo_status": "DONE" } }
3. 客户端接收并处理FCM消息
以Android的FirebaseMessagingService为例,我们需要重写onMessageReceived方法,解析参数、填充本地化模板,最后构建通知:
class TodoFirebaseMessagingService : FirebaseMessagingService() { override fun onMessageReceived(remoteMessage: RemoteMessage) { super.onMessageReceived(remoteMessage) // 解析data中的参数 remoteMessage.data.takeIf { it.isNotEmpty() }?.let { data -> when (data["notification_type"]) { "todo_status_change" -> handleTodoStatusChange(data) // 可以扩展其他通知类型 } } } private fun handleTodoStatusChange(data: Map<String, String>) { // 获取参数,做空值处理避免崩溃 val todoTitle = data["todo_title"] ?: getString(R.string.unknown_todo) val rawStatus = data["todo_status"] ?: "" // 将原始状态值映射为本地化文本 val localizedStatus = when (rawStatus) { "OPEN" -> getString(R.string.status_open) "IN_PROGRESS" -> getString(R.string.status_in_progress) "DONE" -> getString(R.string.status_done) else -> rawStatus // 兜底处理未知状态 } // 填充本地化模板 val notificationContent = getString(R.string.todo_status_changed, todoTitle, localizedStatus) // 构建并显示通知 showNotification(getString(R.string.todo_update_title), notificationContent) } private fun showNotification(title: String, content: String) { val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager val channelId = "todo_notification_channel" // Android O及以上需要创建通知渠道 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel( channelId, "待办事项通知", NotificationManager.IMPORTANCE_DEFAULT ) notificationManager.createNotificationChannel(channel) } val notification = NotificationCompat.Builder(this, channelId) .setSmallIcon(R.drawable.ic_todo_notification) .setContentTitle(title) .setContentText(content) .setPriority(NotificationCompat.PRIORITY_DEFAULT) .setAutoCancel(true) .build() // 用当前时间作为通知ID,避免重复通知被覆盖 notificationManager.notify(System.currentTimeMillis().toInt(), notification) } }
关键注意事项
- 必须用data payload:如果用notification payload,消息会直接被系统通知栏展示,客户端无法拦截处理本地化逻辑;只有data payload会传递到
FirebaseMessagingService,让我们有机会自定义内容。 - 参数校验:客户端一定要处理参数为空的情况,比如给
todo_title设置默认值(比如「未知待办」),避免空指针崩溃。 - 状态枚举一致性:服务端发送的
todo_status值必须和客户端的映射逻辑完全一致,比如服务端不能发completed而客户端只处理DONE,否则会显示原始状态值。 - iOS适配:iOS端的处理逻辑和Android一致,接收FCM的data消息后,用
NSLocalizedString填充模板字符串即可,注意占位符的语法差异(Android用%1$s,iOS用%1$@)。
内容的提问来源于stack exchange,提问作者vonovak
相关产品推荐
相关产品推荐

