From 0b9c91437d83e0ae2606c25d3fa4ba633703599f Mon Sep 17 00:00:00 2001 From: Robby Surya Pratama Date: Tue, 12 Nov 2024 16:05:16 +0700 Subject: [PATCH 1/2] feature: add support for rich text --- lib/src/localization.dart | 56 +++++++++++++++++++++++++++++++++++++-- lib/src/public_ext.dart | 16 +++++++++++ 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/lib/src/localization.dart b/lib/src/localization.dart index 55ef2f3c..02e56cf1 100644 --- a/lib/src/localization.dart +++ b/lib/src/localization.dart @@ -9,6 +9,7 @@ class Localization { late Locale _locale; final RegExp _replaceArgRegex = RegExp('{}'); + final RegExp _namedArgMatcher = RegExp('({([a-zA-Z0-9_]+)})'); final RegExp _linkKeyMatcher = RegExp(r'(?:@(?:\.[a-z]+)?:(?:[\w\-_|.]+|\([\w\-_|.]+\)))'); final RegExp _linkKeyPrefixMatcher = RegExp(r'^@(?:\.([a-z]+))?:'); @@ -68,6 +69,27 @@ class Localization { return _replaceArgs(res, args); } + TextSpan trSpan( + String key, { + Map? namedArgs, + String? gender, + }) { + late String res; + late TextSpan span; + + if (gender != null) { + res = _gender(key, gender: gender); + } else { + res = _resolve(key); + } + + res = _replaceLinks(res); + + span = _replaceSpanNamedArgs(res, namedArgs); + + return span; + } + String _replaceLinks(String res, {bool logging = true}) { // TODO: add recursion detection and a resolve stack. final matches = _linkKeyMatcher.allMatches(res); @@ -103,6 +125,24 @@ class Localization { return result; } + List _splitTextWithNamedArg(String text) { + final matches = _namedArgMatcher.allMatches(text); + var lastIndex = 0; + final result = []; + + for (final match in matches) { + result + ..add(text.substring(lastIndex, match.start)) + ..add(match.group(0) ?? ''); + lastIndex = match.end; + } + if (lastIndex < text.length) { + result.add(text.substring(lastIndex)); + } + + return result; + } + String _replaceArgs(String res, List? args) { if (args == null || args.isEmpty) return res; for (var str in args) { @@ -118,6 +158,16 @@ class Localization { return res; } + + TextSpan _replaceSpanNamedArgs(String res, Map? args) { + if (args == null || args.isEmpty) return TextSpan(text: res); + final spans = _splitTextWithNamedArg(res).map((part) { + final key = part.replaceAll(RegExp(r'^\{|\}$'), ''); + return args[key] ?? TextSpan(text: part); + }).toList(); + return TextSpan(children: spans); + } + static PluralRule? _pluralRule(String? locale, num howMany) { if (instance._ignorePluralRules) { return () => _pluralCaseFallback(howMany); @@ -150,7 +200,8 @@ class Localization { late String res; final pluralRule = _pluralRule(_locale.languageCode, value); - final pluralCase = pluralRule != null ? pluralRule() : _pluralCaseFallback(value); + final pluralCase = + pluralRule != null ? pluralRule() : _pluralCaseFallback(value); switch (pluralCase) { case PluralCase.ZERO: @@ -193,7 +244,8 @@ class Localization { if (subKey == 'other') return _resolve('$key.other'); final tag = '$key.$subKey'; - var resource = _resolve(tag, logging: false, fallback: _fallbackTranslations != null); + var resource = + _resolve(tag, logging: false, fallback: _fallbackTranslations != null); if (resource == tag) { resource = _resolve('$key.other'); } diff --git a/lib/src/public_ext.dart b/lib/src/public_ext.dart index 4b1aa878..fc5f8c5a 100644 --- a/lib/src/public_ext.dart +++ b/lib/src/public_ext.dart @@ -219,6 +219,22 @@ extension BuildContextEasyLocalizationExtension on BuildContext { ); } + TextSpan trSpan( + String key, { + Map? namedArgs, + }) { + final localization = Localization.of(this); + + if (localization == null) { + throw const LocalizationNotFoundException(); + } + + return localization.trSpan( + key, + namedArgs: namedArgs, + ); + } + String plural( String key, num number, { From c5416daa0574a696c344dd5cd4f76d440d40df01 Mon Sep 17 00:00:00 2001 From: Robby Surya Pratama Date: Tue, 12 Nov 2024 16:28:51 +0700 Subject: [PATCH 2/2] feature: add trSpan to public function --- lib/src/localization.dart | 8 +------- lib/src/public.dart | 31 +++++++++++++++++++++++++++++-- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/lib/src/localization.dart b/lib/src/localization.dart index 02e56cf1..c36b7abc 100644 --- a/lib/src/localization.dart +++ b/lib/src/localization.dart @@ -72,16 +72,11 @@ class Localization { TextSpan trSpan( String key, { Map? namedArgs, - String? gender, }) { late String res; late TextSpan span; - if (gender != null) { - res = _gender(key, gender: gender); - } else { - res = _resolve(key); - } + res = _resolve(key); res = _replaceLinks(res); @@ -158,7 +153,6 @@ class Localization { return res; } - TextSpan _replaceSpanNamedArgs(String res, Map? args) { if (args == null || args.isEmpty) return TextSpan(text: res); final spans = _splitTextWithNamedArg(res).map((part) { diff --git a/lib/src/public.dart b/lib/src/public.dart index ba419496..b525bb82 100644 --- a/lib/src/public.dart +++ b/lib/src/public.dart @@ -45,9 +45,36 @@ String tr( .tr(key, args: args, namedArgs: namedArgs, gender: gender); } +/// {@template trSpan} +/// function for translate your language keys +/// [key] Localization key +/// [BuildContext] The location in the tree where this widget builds +/// [namedArgs] Map of localized strings. Replaces the name keys {key_name} with [TextSpan] +/// +/// Example: +/// +/// ```json +/// { +/// "msg_named":"Easy localization is written in the {lang} language" +/// } +/// ``` +/// ```dart +/// Text.rich(trSpan('msg_named',namedArgs: {'lang': TextSpan(text: 'Dart')})), +/// ``` +/// {@endtemplate} + +TextSpan trSpan( + String key, { + BuildContext? context, + Map? namedArgs, +}) { + return context != null + ? Localization.of(context)!.trSpan(key, namedArgs: namedArgs) + : Localization.instance.trSpan(key, namedArgs: namedArgs); +} + bool trExists(String key) { - return Localization.instance - .exists(key); + return Localization.instance.exists(key); } /// {@template plural}