You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在firebase-messaging-sw.js中访问localStorage实现FCM通知多语言

报错原因

firebase-messaging-sw.js运行在独立的Service Worker线程上下文,没有主线程的window全局对象,而localStorage是挂载在window下的同步API,在Service Worker中完全不存在,因此会抛出localStorage is not defined的错误。

另外原代码还有一个隐藏bug:当推送title不是new order时,translate()直接返回body字符串,后续取.body属性会得到undefined,导致通知正文丢失。

实现方案

推荐用主线程同步语言配置 + Service Worker端IndexedDB持久化的方案,不需要改动后端推送逻辑,兼容性最好。

步骤1:主线程(Vue项目代码)同步语言配置

Service Worker只支持异步存储API,无法直接读localStorage,需要在用户切换语言、Service Worker注册完成两个时机,把当前选中的语言通过postMessage同步给Service Worker:

  • 语言切换逻辑中加入同步代码
// 你原有的语言切换逻辑
const changeLang = async (selectedLang) => {
  localStorage.setItem('language', selectedLang)
  // 新增:同步语言配置给Service Worker
  if ('serviceWorker' in navigator) {
    const registration = await navigator.serviceWorker.getRegistration()
    if (registration?.active) {
      registration.active.postMessage({
        type: 'SET_LANG',
        lang: selectedLang
      })
    }
  }
}
  • Service Worker注册成功后首次同步当前语言
// 你原有的注册Firebase Service Worker的逻辑
if ('serviceWorker' in navigator) {
  const registration = await navigator.serviceWorker.register('/firebase-messaging-sw.js')
  // 新增:首次注册时同步当前已存的语言
  const currentLang = localStorage.getItem('language') || 'ar'
  if (registration.active) {
    registration.active.postMessage({ type: 'SET_LANG', lang: currentLang })
  }
}

步骤2:修改firebase-messaging-sw.js逻辑

在SW文件中加入极简IndexedDB存储工具,持久化主线程同步过来的语言配置,收到后台推送时读取配置做文案翻译:

// 开头加:极简IndexedDB键值对存储工具,无需引入第三方库
const DB_NAME = 'app-config'
const STORE_NAME = 'settings'
const openDB = () => {
  return new Promise((resolve, reject) => {
    const req = indexedDB.open(DB_NAME, 1)
    req.onupgradeneeded = () => req.result.createObjectStore(STORE_NAME)
    req.onsuccess = () => resolve(req.result)
    req.onerror = () => reject(req.error)
  })
}
const setItem = async (key, value) => {
  const db = await openDB()
  return new Promise((resolve, reject) => {
    const tx = db.transaction(STORE_NAME, 'readwrite')
    tx.objectStore(STORE_NAME).put(value, key)
    tx.oncomplete = () => resolve()
    tx.onerror = () => reject(tx.error)
  })
}
const getItem = async (key) => {
  const db = await openDB()
  return new Promise((resolve, reject) => {
    const tx = db.transaction(STORE_NAME, 'readonly')
    const req = tx.objectStore(STORE_NAME).get(key)
    req.onsuccess = () => resolve(req.result)
    req.onerror = () => reject(req.error)
  })
}

// 监听主线程发来的配置更新
self.addEventListener('message', async (event) => {
  if (event.data?.type === 'SET_LANG') {
    await setItem('language', event.data.lang)
  }
})

// 修改原有的onBackgroundMessage逻辑为异步
firebase.messaging().onBackgroundMessage(async (payload) => {
  const { title, body, icon, ...restPayload } = payload.data;
  // 读取持久化存储的语言,默认阿拉伯语
  const savedLang = await getItem('language') || 'ar'
  const fr = savedLang === "fr";

  const translate = () => {
    if (title == "new order") {
      return {
        title: fr?"Nouvelle commande":"طلبية جديدة",
        body: fr?`La commande a le code ${body}`:`الطلبية تحمل الرمز ${body}`,
      };
    } else {
      // 修复原代码的bug,统一返回对象格式
      return { title, body };
    }
  };
  const translated = translate()
  const options = {
    data: restPayload,
    body: translated.body,
    icon: "img/icons/android-chrome-192x192.png",
    badge: "img/icons/badge-48x48.png",
    vibrate: [400, 100, 400, 100, 400, 100, 400],
    dir: fr?"ltr":"rtl",
    lang: fr?"fr":"ar",
  };

  return self.registration.showNotification(translated.title, options);
});

可选方案:后端携带多语言文案

如果可以调整推送服务逻辑,也可以在发送FCM消息时直接把所有支持语言的文案放在payload中,SW端只需要读取本地存的语言,直接取对应字段即可,不需要在SW里维护翻译映射表,后续新增语言不需要重新发布Service Worker文件。


内容的提问来源于stack exchange,提问作者Adam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 21:06:34