Flutter国际化之查找中文变量

最近在做国际化相关内容,要求在已经使用的项目中,查找出所有带汉字的变量或常量,然后进行国际化处理。人工查找费力不讨好,就想着能不能通过ast来查询子节点,找出所有需要的节点。

首先该dart脚本必须放于项目下,根据然后递归查找到该项目的根目录。这时候就出现一个问题,

项目中如果存在了临时文件(.g.dart)、测试的dart文件、或者第三方库的dart文件,则需要过滤。需要新建一个文件,将不要扫描的路径加入,加入后,遍历到路径后就不会进行查找。

///不进行扫描的路径
List<String> excludePath = [];

void main(List<String> arguments) {
  Map<String, _LocationInfo> chineseSet = {};

  ///检索是否文件目录存在项目名,可以通过参数输入
  final projectPath = arguments.isEmpty ? project : arguments.first;
  Directory current = Directory.current;
  var path = current.path;

  ///项目的目录
  String targetPath;

  while (path != "/" && !path.split("/").last.contains(projectPath)) {
    current = current.parent;
    path = current.path;
  }
  targetPath = path;
  if (path.isEmpty) {
    print('执行的dart文件必须属于项目中');
    return;
  }
  excludePath = File("$targetPath/.pathignore")
      .readAsLinesSync()
      .map((e) => "$targetPath$e")
      .toList();
  findByDart(chineseSet, targetPath);
  if (chineseSet.isEmpty) {
    print("没有可读取中文,停止操作");
    return;
  }
  writeToExcel(chineseSet, targetPath);
}

之后就是findByDart方法,该方法实现了从项目根目录,进行递归查找dart文件的逻辑,并过滤了.g.dart这种文件的过滤,一但查找到dart文件,将使用ast对该文件进行解析。

///递归查找所有路径下的dart文件
void findByDart(Map<String, _LocationInfo> chineseSet, String targetPath) {
  Directory(targetPath).listSync().forEach((element) {
    ///如果是忽略目录的路径,则略过该目录,且不在扫描其子目录
    if (excludePath.contains(element.path)) {
      print('忽略扫描路径:${element.path}');
    } else {
      ///如果是路径,则递归调用
      if (FileSystemEntity.isDirectorySync(element.path)) {
        findDartFile(chineseSet, element.path);
      } else if (FileSystemEntity.isFileSync(element.path)) {
        File file = File(element.path);
        print('当前扫描文件路径:${element.path}');

        ///判定该文件是dart文件,进行解析
        ///忽略.g.dart文件
        if (!file.uri.pathSegments.last.contains(".g.dart") &&
            file.uri.pathSegments.last.contains(".dart")) {
          ParseStringResult result = parseFile(
              path: file.path, featureSet: FeatureSet.latestLanguageVersion());
          result.unit
              .accept(ChineseVisitor(chineseSet, element.path, result.unit));
        }
      }
    }
  });
}

然后就是ast节点的判断,判断节点类型是否是简单字符串类型或者是插值字符串类型,并判定是否包含中文的节点,如果判定成功,则过滤掉引号。并通过analysis库,将该节点在文件的行列进行输出,具体Ast的API可以查看analysis库。

class ChineseVisitor extends GeneralizingAstVisitor {
  ChineseVisitor(this.chineseSet, this.filePath, this.unit);

  RegExp chineseExp = RegExp(r"[\u4e00-\u9fa5]");

  String filePath;

  final CompilationUnit unit;

  final Map<String, _LocationInfo> chineseSet;

  @override
  dynamic visitNode(AstNode node) {
    ///只解析字符串常量值与插入值,是否含中文
    if (node.runtimeType.toString() == "SimpleStringLiteralImpl" ||
        node.runtimeType.toString() == "InterpolationStringImpl") {
      if (chineseExp.hasMatch(node.toSource())) {
        ///过滤引号
        var sourceValue =
            node.toSource().replaceAll("'", "").replaceAll('"', "").toString();
        var location = unit.lineInfo.getLocation(node.offset);
        chineseSet[sourceValue] =
            _LocationInfo(filePath, location.lineNumber, location.columnNumber);
        // print('${node.runtimeType}->$sourceValue');
      }
    }
    return super.visitNode(node);
  }
}

class _LocationInfo {
  _LocationInfo(this.path, this.lineNumber, this.columnNumber);

  ///所属文件
  String path;

  ///所在行
  int lineNumber;

  ///所在列
  int columnNumber;
}

之后将获取的信息输出到excel文件中,等待后续处理,具体实现参考excel库即可。

void writeToExcel(Map<String, _LocationInfo> chineseSet, String targetPath) {
  var excel = Excel.createExcel();
  excel.rename('Sheet1', 'translate');
  var sheet = excel['translate'];

  var cellA1 = sheet.cell(CellIndex.indexByString("A1"));
  cellA1.value = "项目中的中文:";

  var cellB1 = sheet.cell(CellIndex.indexByString("B1"));
  cellB1.value = "所在文件位置:";

  var cellC1 = sheet.cell(CellIndex.indexByString("C1"));
  cellC1.value = "所在行:";

  var cellD1 = sheet.cell(CellIndex.indexByString("D1"));
  cellD1.value = "所在列:";

  ///A1作为标题
  ///从A2开始到AN为待翻译中文
  for (int row = 2; row < chineseSet.length + 2; row++) {
    sheet.cell(CellIndex.indexByString("A$row")).value =
        chineseSet.keys.toList()[row - 2];
    sheet.cell(CellIndex.indexByString("B$row")).value =
        chineseSet.values.toList()[row - 2].path.replaceAll(targetPath, "");
    sheet.cell(CellIndex.indexByString("C$row")).value =
        chineseSet.values.toList()[row - 2].lineNumber;
    sheet.cell(CellIndex.indexByString("D$row")).value =
        chineseSet.values.toList()[row - 2].columnNumber;
  }

  var fileBytes = excel.save();

  ///文件保存在
  if (fileBytes != null) {
    var targetExcel = File("$targetPath/filter_chinese.xlsx");
    if (targetExcel.existsSync()) {
      targetExcel.deleteSync();
    }
    File("$targetPath/filter_chinese.xlsx")
      ..createSync(recursive: true)
      ..writeAsBytesSync(fileBytes);
    print("成功写入到$targetPath/filter_chinese.xlsx");
  }
}

该实现方式可以查找到所有包含中文的变量,缺点是有可能会遍历出一些使用中,被变量分割的情况,该情况被输出到excel时,需要根据输出的变量位置,实际考虑所用变量的语境。

输出到excel中的内容,自动化进行翻译等过程,本文不与操作,写个脚本即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WxSkyqi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值