02月20, 2021

为基于Electron开发的Mac端本地应用启用别名

背景交代

最近在做的一个基于Electron开发的Mac端项目,要进行产品更名。

alt

但是如果直接修改编译打包后的应用名称,问题会比较大。

alt

首先,类似上图里所有缓存资源、以及涉及鉴权的各种Cookie&Storage ,都是存放在类似/Users/huzunjie/Library/Application Support/旧名称目录下的。

其次,通过旧名称.dmg安装到启动台中的应用,对应物理目录为/Applications/旧名称.app

如果直接对应用打包时更名,缓存必然全部失效,也会导致/Applications/目录下同时存在新旧2个不同名称的APP。

这显然不是想看到的结果。

解决方案

通过查询资料,我们发现实际MacOS是允许在不影响原/Applications/旧名称.app物理路径的基础上,给应用设置一系列语言包,用于告知MacOS的文件系统及启动台等地方,按本地化语言环境展示对应名字。

这么以来也就找到了一个“为编译打包旧名称.app中增加语言包来配置别名”的解决方案。

我是基于 electron-build进行编译打包的,所以按上面这个思路,给package.json增加语言配置,并另外基于electron-buildafterPack Hook实现了个补充语言包的脚本。

具体代码

首先 package.json 中要添加 electronLanguagesInfoPlistStrings 配置,及钩子脚本afterPack、并在extendInfo中添加LSHasLocalizedDisplayName字段:

{
  "name": "my-app-name",
  "productName": "旧名称",
  ....
  "electronLanguagesInfoPlistStrings": {
    "en": {
      "CFBundleDisplayName": "En新名称",
      "CFBundleName": "En新名称"
    },
    "zh_CN": {
      "CFBundleDisplayName": "新名称",
      "CFBundleName": "新名称"
    },
  },
  "build": {
    "afterPack": "./buildAfterPack.js",
    "mac": {
      "extendInfo": {
        "LSHasLocalizedDisplayName": true
      }
    }
    ....
  }
  ....
}

编译时的钩子脚本./buildAfterPack.js用于读取package.json中的语言配置,并生成语言包物理文件,实现如下 :

/* 基于 APP 打包后的钩子功能补充语言包配置,以满足类似“旧名字”在Mac文件系统中显示为“新名字”的需求 */
const fs = require('fs');

exports.default = async (context) => {
  const { electronPlatformName, appOutDir } = context;
  if (electronPlatformName !== 'darwin') {
    return;
  }
  const {
    productFilename,
    info: {
      _metadata: { electronLanguagesInfoPlistStrings },
    },
  } = context.packager.appInfo;

  const resPath = `${appOutDir}/${productFilename}.app/Contents/Resources/`;
  console.log(
    '\n> 基于 package.json 配置项 “electronLanguagesInfoPlistStrings” 创建语言包 Sta \n',
    '\n>  electronLanguagesInfoPlistStrings:\n',
    electronLanguagesInfoPlistStrings,
    '\n\n',
    '>  ResourcesPath:',
    resPath
  );

  // 创建APP语言包文件
  const createLangFilesPromise = await Promise.all(
    Object.keys(electronLanguagesInfoPlistStrings).map((langKey) => {
      const infoPlistStrPath = `${langKey}.lproj/InfoPlist.strings`;
      let infos = '';
      const langItem = electronLanguagesInfoPlistStrings[langKey];
      Object.keys(langItem).forEach((infoKey) => {
        infos += `"${infoKey}" = "${langItem[infoKey]}";\n`;
      });
      return new Promise((resolve) => {
        fs.writeFile(`${resPath}${infoPlistStrPath}`, infos, (err) => {
          resolve();
          if (err) throw err;
          console.log(`>  “{ResourcesPath}/${infoPlistStrPath}” 创建完毕。`);
        });
      });
    })
  );
  console.log(
    '\n> 基于 package.json 配置项 “electronLanguagesInfoPlistStrings” 创建语言包 End \n'
  );
  return createLangFilesPromise;
};

补充配置

以上解决了安装后的应用更名需求。

但编译打包后的文件还是“旧名称 1.2.3.dmg”、“旧名称 1.2.3.zip”。

用户安装过程中操作界面上大部分显示的也是“旧名称.app”。

alt

如果要保证编译产生文件及上图中“旧名称”显示为新名称,还需要继续修改 package.json,按下面位置添加 artifactNamedmg.title:

{
  ....
  "build": {
    "artifactName": "新名称-${version}.${ext}",
    "dmg": {
      "title": "新名称 ${version}",
       ...
    }
    ....
  }
  ....
}

后记

这确实解决了我遇到的需求,但未必是最好的解决方案,如果发现有疏忽的地方或有更好的方案,欢迎回复。

本文链接:http://blog.pyzy.net/post/electron-app-display-name-mac.html

-- EOF --

Comments

可以发邮件 huzunjie@pyzy.net 或移步到 https://github.com/huzunjie/blog.pyzy.net/issues 评论交流。