【Flutter多语言翻译】一句话翻译返回多个widget

dart 复制代码
/// [getTranslate]处理多语言结构,返回Widget
/// 示例讲解:
/// lib/l10n/app_zh.arb 和 lib/l10n/app_en.arb 文件中:
/// 1. 使用"{}"包裹占位符,用官方方法解析,AppLocalizations.of(context).wakeup('学习服务') 返回的是纯String,
/// 即:"唤醒学习服务"。
/// ```json
///   "wakeup": "唤醒{svcType}",
///   "@wakeup": {
///     "description": "唤醒学习服务",
///     "placeholders": {
///       "svcType": {
///         "type": "String",
///         "example": "学习服务"
///       }
///     }
///   },
/// ```
///
/// 2. 使用"<??>"包裹占位符,结合[getTranslate]解析,会将 String 切割为 List<String>
/// 以<??>"为切割点",
/// "doAction": "列举几个操作:<?action1?>和<?action2?>以及<?buyHint2?>,你看如何?",
/// 切割后示例:
/// ["列举几个操作:", "<?action1?>","和","<?action1?>","以及","<?buyHint2?>", ",你看如何?"]
///
/// ```dart
///   getTranslate(
///     text: AppLocalizations.of(context).doAction,
///     defaultTextStyle: TextStyle(color: Colors.blue), // "列举几个操作:","和","以及", ",你看如何?" 这些文字显示为Colors.blue
///     hightlightTextStyle: TextStyle(color: Colors.yellow), // <?action1?>的内容显示为Colors.yellow
///     keyValue: {
///       "action1": AppLocalizations.of(context).xxx, // 纯文字,使用hightlightTextStyle
///       "action2": TextButton(child: Text(AppLocalizations.of(context).xxx, textStyle: TextStyle(color: Colors.orange)), onPressed() {}), // TextButton自带交互,不使用hightlightTextStyle
///       "buyHint2": Text(
///         AppLocalizations.of(context).xxx, 
///         textStyle: TextStyle(color: Colors.green, hight:1, decoration: TextDecoration.underline),
///       ), // Text自带交互,不使用hightlightTextStyle
///     },
///   )
/// ```
/// 3. 一句话中,同时存在"{}"和"<??>"包裹占位符
/// [getTranslate]只切割"<??>","{}"包裹的部分由官方自动转为String传入;
/// 示例:
/// "doActionOnDemo": "列举几个操作:{action1}和<?action2?>,你看如何?",
/// "edit": "编辑"
/// ```dart
///   getTranslate(
///     text: AppLocalizations.of(context).doActionOnDemo('登录账号'),
///     defaultTextStyle: TextStyle(color: Colors.blue), // "列举几个操作:登录账号"(action1已被官方方法翻译为'登录账号'),显示为Colors.blue
///     hightlightTextStyle: TextStyle(color: Colors.yellow), // <?action1?>的内容显示为Colors.yellow
///     keyValue: {
///       "action2": AppLocalizations.of(context).edit, // "编辑",显示为Colors.yellow
///     },
///   )
/// ```
/// 参数说明
/// @param String [str]:
///   需要翻译的AppLocalizations.of(context).xxx
/// @param Map<String, String> [keyValue]:
///   keyValue的[key]需与"<??>"包裹的占位符一致,例如:"这是占位符示例<?demo?>",key就是"demo";
///   keyValue的[value]是<?demo?>翻译后的String值,或者是一个Widget;
/// @param TextStyle [defaultTextStyle] :
///   常规文字的样式
/// @param TextStyle [hightlightTextStyle] :
///   高亮文字的样式(仅当keyValue传入的value是String时生效,如果value传入的是Widget就不生效)
///
Widget getTranslate({
  required String str,
  required Map keyValue,
  TextStyle? defaultTextStyle,
  TextStyle? highlightTextStyle,
}) {
  /// "列举几个操作:{buyHint}和<?buyHint2?>,你看如何?",
  RegExp rExp = RegExp(r'<\?(.+?)\?>'); // 去匹配<??>中间的内容
  var lastIndex = 0;
  var result = rExp.allMatches(str);
  List<InlineSpan> widgetList = [];
  for (var match in result) {
    // 将匹配前的部分加入到结果中
    if (match.start > lastIndex) {
      var firstText = str.substring(lastIndex, match.start);
      widgetList.add(TextSpan(text: firstText, style: defaultTextStyle));
    }
    // 关键字替换
    String key = match.group(1) ?? ''; // key的匹配结果是'action1'/'action2'/'buyHint2'
    if (keyValue[key] != null) {
      if (keyValue[key] is String) {
        // 如果keyValue的value传入的是String文字,使用TextSpan设置高亮色
        widgetList.add(TextSpan(text: keyValue[key], style: highlightTextStyle));
      } else if (keyValue[key] is Widget) {
        // 如果keyValue的value传入的是Widget,使用WidgetSpan包裹Widget,样式需要外部写
        widgetList.add(WidgetSpan(child: keyValue[key]));
      }
    }

    // 更新lastIndex为当前匹配项的结束位置
    lastIndex = match.end;
  }
  // 将剩余的非匹配部分添加到结果中
  if (lastIndex < str.length) {
    var lastText = str.substring(lastIndex);
    widgetList.add(TextSpan(text: lastText, style: defaultTextStyle));
  }

  return Text.rich(TextSpan(children: widgetList));
}

封装成一个TranslateWidget控件

dart 复制代码
class TranslateWidget extends StatelessWidget{
    const TranslateWidget({
		super.key,
		required String str,
		this.keyValue,
		this.defaultTextStyle,
  		this.highlightTextStyle,
	});
	final String str,
  	final Map keyValue,
    final TextStyle? defaultTextStyle,
    final TextStyle? highlightTextStyle,

	@override
	Widget build(BuildContext context) {
		return getTranslate(
			str: str,
  			keyValue: keyValue,
  			defaultTextStyle: defaultTextStyle,
  			highlightTextStyle: highlightTextStyle,
		);
	}
}

运用TranslateWidget:

dart 复制代码
// zh.arb
{
	"demoSentence": "这是一个句子,首先展示第一个示例:{demo1},接着展示第二个示例:<?demo2?>,最后展示第三个示例:<?demo3?>",
	"@demo1": {
		"description": "这是一个句子,首先展示第一个示例:随意填写的原生示例1,接着展示第二个示例:<?demo2?>,最后展示第三个示例:<?demo3?>",
     	"placeholders": {
     	  	"demo1": {
      	  		"type": "String",
				"example": "随意填写的原生示例1"
			}
		}
 	},
	"demo1": "原生示例1",
	"demo2": "自定义示例2",
    "demo3": "自定义示例3"
}
dart 复制代码
// 某个widget中
Widget build (BuildContext context) {
	var lang = AppLocalizations.of(context);
	return Container(
	    padding: const EdgeInsets.all(10),
    	child: TranslateWidget(
			str: lang!.demoSentence(lang.demo1),
			keyValue: {
				"demo2": Text(lang!.demo2, textStyle: TextStyle(color: Colors.red,backgroundColor: Colors.pink)),
				"demo3": lang!.demo3
			},
			defaultTextStyle: TextStyle(color: Colors.black),
			highlightTextStyle: TextStyle(backgroundColor: Colors.yellow),
		),
	),
}

显示效果大概如下 ↓↓↓ :


这是一个句子,首先展示第一个示例:原生示例1,接着展示第二个示例:自定义示例2,最后展示第三个示例:自定义示例3


文章从 TranslateWidget 控件开始,是根据思路手打出来的,没有运行过,可能有疏漏导致报错。

  1. <??> 是作者认为比较舒服的标记,标记可由读者自行定义
  2. getTranslate 方法,需要搭配多语言的arb文件一起使用。
  3. 字体行高需要手动调整,否则一句话可能无法垂直对齐
相关推荐
明君8799716 分钟前
#Flutter 的官方Skills技能库
前端·flutter
恋猫de小郭2 小时前
谷歌 Genkit Dart 正式发布:现在可以使用 Dart 和 Flutter 构建全栈 AI 应用
android·前端·flutter
恋猫de小郭1 天前
你还用 IDE 吗? AI 狂欢时代下 Cursor 慌了, JetBrains 等 IDE 的未来是什么?
前端·flutter·ai编程
TT_Close2 天前
🐟 发布中心进度同步:8 个商店的上传功能开发完毕,正抓紧测试
flutter·npm·visual studio code
RaidenLiu2 天前
Flutter Platform Channel 底层架构解析 —— 从 BinaryMessenger 到跨平台消息通信机制
前端·flutter·前端框架
鹏多多2 天前
Flutter使用screenshot进行截屏和截长图以及分享保存的全流程指南
android·前端·flutter
恋猫de小郭2 天前
什么 AI 写 Android 最好用?官方做了一个基准测试排名
android·前端·flutter
勤劳打代码4 天前
Flutter 架构日记 — 状态管理
flutter·架构·前端框架
比特鹰5 天前
手把手带你用Flutter手搓人生K线
前端·javascript·flutter
火柴就是我5 天前
Flutter限制输入框只能输入中文,iOS拼音打不出来?
flutter