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 控件开始,是根据思路手打出来的,没有运行过,可能有疏漏导致报错。
- <??> 是作者认为比较舒服的标记,标记可由读者自行定义
- getTranslate 方法,需要搭配多语言的arb文件一起使用。
- 字体行高需要手动调整,否则一句话可能无法垂直对齐