diff --git a/lib/config/themes/tb_theme.dart b/lib/config/themes/tb_theme.dart index b89e4ba..750156a 100644 --- a/lib/config/themes/tb_theme.dart +++ b/lib/config/themes/tb_theme.dart @@ -43,7 +43,7 @@ const tbDarkMatIndigo = MaterialColor( }, ); -final ThemeData theme = ThemeData(); +final ThemeData theme = ThemeData(primarySwatch: tbMatIndigo); ThemeData tbTheme = ThemeData( primarySwatch: tbMatIndigo, @@ -73,7 +73,10 @@ ThemeData tbTheme = ThemeData( TargetPlatform.android: FadeOpenPageTransitionsBuilder(), })); +final ThemeData darkTheme = + ThemeData(primarySwatch: tbDarkMatIndigo, brightness: Brightness.dark); + ThemeData tbDarkTheme = ThemeData( primarySwatch: tbDarkMatIndigo, - colorScheme: theme.colorScheme.copyWith(secondary: Colors.deepOrange), + colorScheme: darkTheme.colorScheme.copyWith(secondary: Colors.deepOrange), brightness: Brightness.dark); diff --git a/lib/core/auth/login/login_page.dart b/lib/core/auth/login/login_page.dart index 4afec88..2cc49c5 100644 --- a/lib/core/auth/login/login_page.dart +++ b/lib/core/auth/login/login_page.dart @@ -8,6 +8,7 @@ import 'package:material_design_icons_flutter/material_design_icons_flutter.dart import 'package:thingsboard_app/constants/assets_path.dart'; import 'package:thingsboard_app/core/context/tb_context.dart'; import 'package:thingsboard_app/core/context/tb_context_widget.dart'; +import 'package:thingsboard_app/generated/l10n.dart'; import 'package:thingsboard_app/widgets/tb_progress_indicator.dart'; import 'package:thingsboard_client/thingsboard_client.dart'; @@ -67,11 +68,12 @@ class _LoginPageState extends TbPageState { ThingsboardImage.thingsBoardWithTitle, height: 25, color: Theme.of(context).primaryColor, - semanticsLabel: 'ThingsBoard Logo') + semanticsLabel: + '${S.of(context).logoDefaultValue}') ]), SizedBox(height: 32), Row(children: [ - Text('Login to your account', + Text('${S.of(context).loginNotification}', style: TextStyle( fontWeight: FontWeight.bold, fontSize: 28, @@ -91,7 +93,7 @@ class _LoginPageState extends TbPageState { Padding( padding: EdgeInsets.symmetric( horizontal: 16), - child: Text('OR'), + child: Text('${S.of(context).OR}'), ), Flexible(child: Divider()) ], @@ -108,14 +110,16 @@ class _LoginPageState extends TbPageState { validator: FormBuilderValidators.compose([ FormBuilderValidators.required( - errorText: 'Email is required.'), + errorText: + '${S.of(context).emailRequireText}'), FormBuilderValidators.email( errorText: - 'Invalid email format.') + '${S.of(context).emailInvalidText}') ]), decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'Email'), + labelText: + '${S.of(context).email}'), ), SizedBox(height: 28), ValueListenableBuilder( @@ -130,7 +134,7 @@ class _LoginPageState extends TbPageState { .compose([ FormBuilderValidators.required( errorText: - 'Password is required.') + '${S.of(context).passwordRequireText}') ]), decoration: InputDecoration( suffixIcon: IconButton( @@ -145,7 +149,8 @@ class _LoginPageState extends TbPageState { }, ), border: OutlineInputBorder(), - labelText: 'Password'), + labelText: + '${S.of(context).password}'), ); }) ], @@ -158,7 +163,7 @@ class _LoginPageState extends TbPageState { _forgotPassword(); }, child: Text( - 'Forgot Password?', + '${S.of(context).passwordForgotText}', style: TextStyle( color: Theme.of(context) .colorScheme @@ -172,7 +177,7 @@ class _LoginPageState extends TbPageState { ), Spacer(), ElevatedButton( - child: Text('Log In'), + child: Text('${S.of(context).login}'), style: ElevatedButton.styleFrom( padding: EdgeInsets.symmetric(vertical: 16)), diff --git a/lib/core/auth/login/reset_password_request_page.dart b/lib/core/auth/login/reset_password_request_page.dart index dc0d2d2..e6e70a4 100644 --- a/lib/core/auth/login/reset_password_request_page.dart +++ b/lib/core/auth/login/reset_password_request_page.dart @@ -4,6 +4,7 @@ import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:thingsboard_app/core/auth/login/login_page_background.dart'; import 'package:thingsboard_app/core/context/tb_context.dart'; import 'package:thingsboard_app/core/context/tb_context_widget.dart'; +import 'package:thingsboard_app/generated/l10n.dart'; import 'package:thingsboard_app/widgets/tb_app_bar.dart'; import 'package:thingsboard_app/widgets/tb_progress_indicator.dart'; @@ -31,7 +32,7 @@ class _ResetPasswordRequestPageState backgroundColor: Colors.transparent, appBar: TbAppBar( tbContext, - title: Text('Reset password'), + title: Text('${S.of(context).passwordReset}'), ), body: Stack(children: [ SizedBox.expand( @@ -45,7 +46,7 @@ class _ResetPasswordRequestPageState children: [ SizedBox(height: 16), Text( - 'Enter the email associated with your account and we\'ll send an email with password reset link', + '${S.of(context).passwordResetText}', textAlign: TextAlign.center, style: TextStyle( color: Color(0xFFAFAFAF), @@ -58,17 +59,20 @@ class _ResetPasswordRequestPageState autofocus: true, validator: FormBuilderValidators.compose([ FormBuilderValidators.required( - errorText: 'Email is required.'), + errorText: + '${S.of(context).emailRequireText}'), FormBuilderValidators.email( - errorText: 'Invalid email format.') + errorText: + '${S.of(context).emailInvalidText}') ]), decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'Email *'), + labelText: '${S.of(context).email} *'), ), Spacer(), ElevatedButton( - child: Text('Request password reset'), + child: Text( + '${S.of(context).requestPasswordReset}'), style: ElevatedButton.styleFrom( padding: EdgeInsets.symmetric(vertical: 16)), @@ -104,7 +108,8 @@ class _ResetPasswordRequestPageState await Future.delayed(Duration(milliseconds: 300)); await tbClient.sendResetPasswordLink(email); _isLoadingNotifier.value = false; - showSuccessNotification('Password reset link was successfully sent!'); + showSuccessNotification( + '${S.of(context).passwordResetLinkSuccessfullySentNotification}'); } catch (e) { _isLoadingNotifier.value = false; } diff --git a/lib/core/entity/entities_base.dart b/lib/core/entity/entities_base.dart index 07b82b0..9bd7b4a 100644 --- a/lib/core/entity/entities_base.dart +++ b/lib/core/entity/entities_base.dart @@ -5,6 +5,7 @@ import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; import 'package:intl/intl.dart'; import 'package:thingsboard_app/core/context/tb_context.dart'; import 'package:thingsboard_app/core/context/tb_context_widget.dart'; +import 'package:thingsboard_app/generated/l10n.dart'; import 'package:thingsboard_app/utils/utils.dart'; import 'package:thingsboard_client/thingsboard_client.dart'; @@ -54,15 +55,15 @@ mixin EntitiesBase on HasTbContext { Key? getKey(T entity) => null; Widget buildEntityListCard(BuildContext context, T entity) { - return Text('Not implemented!'); + return Text('${S.of(context).notImplemented}'); } Widget buildEntityListWidgetCard(BuildContext context, T entity) { - return Text('Not implemented!'); + return Text('${S.of(context).notImplemented}'); } Widget buildEntityGridCard(BuildContext context, T entity) { - return Text('Not implemented!'); + return Text('${S.of(context).notImplemented}'); } double? gridChildAspectRatio() => null; @@ -337,7 +338,7 @@ abstract class BaseEntitiesState Widget noItemsFoundIndicatorBuilder(BuildContext context) { return FirstPageExceptionIndicator( title: widget.noItemsFoundText, - message: 'The list is currently empty.', + message: '${S.of(context).listIsEmptyText}', onTryAgain: widget.searchMode ? null : () => pagingController.refresh(), ); } @@ -391,8 +392,8 @@ class FirstPageExceptionIndicator extends StatelessWidget { Icons.refresh, color: Colors.white, ), - label: const Text( - 'Try Again', + label: Text( + '${S.of(context).tryAgain}', style: TextStyle( fontSize: 16, color: Colors.white, diff --git a/lib/generated/intl/messages_all.dart b/lib/generated/intl/messages_all.dart new file mode 100644 index 0000000..2b80816 --- /dev/null +++ b/lib/generated/intl/messages_all.dart @@ -0,0 +1,66 @@ +// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart +// This is a library that looks up messages for specific locales by +// delegating to the appropriate library. + +// Ignore issues from commonly used lints in this file. +// ignore_for_file:implementation_imports, file_names, unnecessary_new +// ignore_for_file:unnecessary_brace_in_string_interps, directives_ordering +// ignore_for_file:argument_type_not_assignable, invalid_assignment +// ignore_for_file:prefer_single_quotes, prefer_generic_function_type_aliases +// ignore_for_file:comment_references + +import 'dart:async'; + +import 'package:intl/intl.dart'; +import 'package:intl/message_lookup_by_library.dart'; +import 'package:intl/src/intl_helpers.dart'; + +import 'messages_en.dart' as messages_en; +import 'messages_zh.dart' as messages_zh; + +typedef Future LibraryLoader(); +Map _deferredLibraries = { + 'en': () => new Future.value(null), + 'zh': () => new Future.value(null), +}; + +MessageLookupByLibrary? _findExact(String localeName) { + switch (localeName) { + case 'en': + return messages_en.messages; + case 'zh': + return messages_zh.messages; + default: + return null; + } +} + +/// User programs should call this before using [localeName] for messages. +Future initializeMessages(String localeName) async { + var availableLocale = Intl.verifiedLocale( + localeName, (locale) => _deferredLibraries[locale] != null, + onFailure: (_) => null); + if (availableLocale == null) { + return new Future.value(false); + } + var lib = _deferredLibraries[availableLocale]; + await (lib == null ? new Future.value(false) : lib()); + initializeInternalMessageLookup(() => new CompositeMessageLookup()); + messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor); + return new Future.value(true); +} + +bool _messagesExistFor(String locale) { + try { + return _findExact(locale) != null; + } catch (e) { + return false; + } +} + +MessageLookupByLibrary? _findGeneratedMessagesFor(String locale) { + var actualLocale = + Intl.verifiedLocale(locale, _messagesExistFor, onFailure: (_) => null); + if (actualLocale == null) return null; + return _findExact(actualLocale); +} diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart new file mode 100644 index 0000000..365c3c6 --- /dev/null +++ b/lib/generated/intl/messages_en.dart @@ -0,0 +1,131 @@ +// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart +// This is a library that provides messages for a en locale. All the +// messages from the main program should be duplicated here with the same +// function name. + +// Ignore issues from commonly used lints in this file. +// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new +// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering +// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases +// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes +// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes + +import 'package:intl/intl.dart'; +import 'package:intl/message_lookup_by_library.dart'; + +final messages = new MessageLookup(); + +typedef String MessageIfAbsent(String messageStr, List args); + +class MessageLookup extends MessageLookupByLibrary { + String get localeName => 'en'; + + final messages = _notInlinedMessages(_notInlinedMessages); + static Map _notInlinedMessages(_) => { + "No": MessageLookupByLibrary.simpleMessage("No"), + "OR": MessageLookupByLibrary.simpleMessage("OR"), + "Yes": MessageLookupByLibrary.simpleMessage("Yes"), + "actionData": MessageLookupByLibrary.simpleMessage("Action data"), + "active": MessageLookupByLibrary.simpleMessage("Active"), + "address": MessageLookupByLibrary.simpleMessage("Address"), + "address2": MessageLookupByLibrary.simpleMessage("Address 2"), + "alarmAcknowledgeText": MessageLookupByLibrary.simpleMessage( + "Are you sure you want to acknowledge Alarm?"), + "alarmAcknowledgeTitle": + MessageLookupByLibrary.simpleMessage("Acknowledge Alarm"), + "alarmClearText": MessageLookupByLibrary.simpleMessage( + "Are you sure you want to clear Alarm?"), + "alarmClearTitle": MessageLookupByLibrary.simpleMessage("Clear Alarm"), + "alarms": MessageLookupByLibrary.simpleMessage("Alarms"), + "allDevices": MessageLookupByLibrary.simpleMessage("All devices"), + "appTitle": MessageLookupByLibrary.simpleMessage("Thingsboard"), + "assetName": MessageLookupByLibrary.simpleMessage("Asset name"), + "assets": MessageLookupByLibrary.simpleMessage("Assets"), + "assignedToCustomer": + MessageLookupByLibrary.simpleMessage("Assigned to customer"), + "auditLogDetails": + MessageLookupByLibrary.simpleMessage("Audit log details"), + "auditLogs": MessageLookupByLibrary.simpleMessage("Audit Logs"), + "changePassword": + MessageLookupByLibrary.simpleMessage("Change Password"), + "city": MessageLookupByLibrary.simpleMessage("City"), + "country": MessageLookupByLibrary.simpleMessage("Country"), + "currentPassword": + MessageLookupByLibrary.simpleMessage("currentPassword"), + "currentPasswordRequireText": MessageLookupByLibrary.simpleMessage( + "Current password is required."), + "currentPasswordStar": + MessageLookupByLibrary.simpleMessage("Current password *"), + "customer": MessageLookupByLibrary.simpleMessage("Customer"), + "customers": MessageLookupByLibrary.simpleMessage("Customers"), + "devices": MessageLookupByLibrary.simpleMessage("Devices"), + "email": MessageLookupByLibrary.simpleMessage("Email"), + "emailInvalidText": + MessageLookupByLibrary.simpleMessage("Invalid email format."), + "emailRequireText": + MessageLookupByLibrary.simpleMessage("Email is required."), + "emailStar": MessageLookupByLibrary.simpleMessage("Email *"), + "entityType": MessageLookupByLibrary.simpleMessage("Entity Type"), + "failureDetails": + MessageLookupByLibrary.simpleMessage("Failure details"), + "firstName": MessageLookupByLibrary.simpleMessage("firstName"), + "firstNameUpper": MessageLookupByLibrary.simpleMessage("First Name"), + "home": MessageLookupByLibrary.simpleMessage("Home"), + "inactive": MessageLookupByLibrary.simpleMessage("Inactive"), + "label": MessageLookupByLibrary.simpleMessage("Label"), + "lastName": MessageLookupByLibrary.simpleMessage("lastName"), + "lastNameUpper": MessageLookupByLibrary.simpleMessage("Last Name"), + "listIsEmptyText": MessageLookupByLibrary.simpleMessage( + "The list is currently empty."), + "login": MessageLookupByLibrary.simpleMessage("Log In"), + "loginNotification": + MessageLookupByLibrary.simpleMessage("Login to your account"), + "logoDefaultValue": + MessageLookupByLibrary.simpleMessage("Thingsboard Logo"), + "logout": MessageLookupByLibrary.simpleMessage("Log Out"), + "more": MessageLookupByLibrary.simpleMessage("More"), + "newPassword": MessageLookupByLibrary.simpleMessage("newPassword"), + "newPassword2": MessageLookupByLibrary.simpleMessage("newPassword2"), + "newPassword2RequireText": MessageLookupByLibrary.simpleMessage( + "New password again is required."), + "newPassword2Star": + MessageLookupByLibrary.simpleMessage("New password again *"), + "newPasswordRequireText": + MessageLookupByLibrary.simpleMessage("New password is required."), + "newPasswordStar": + MessageLookupByLibrary.simpleMessage("New password *"), + "notImplemented": + MessageLookupByLibrary.simpleMessage("Not implemented!"), + "password": MessageLookupByLibrary.simpleMessage("Password"), + "passwordErrorNotification": MessageLookupByLibrary.simpleMessage( + "Entered passwords must be same!"), + "passwordForgotText": + MessageLookupByLibrary.simpleMessage("Forgot Password?"), + "passwordRequireText": + MessageLookupByLibrary.simpleMessage("Password is required."), + "passwordReset": MessageLookupByLibrary.simpleMessage("Reset password"), + "passwordResetLinkSuccessfullySentNotification": + MessageLookupByLibrary.simpleMessage( + "Password reset link was successfully sent!"), + "passwordResetText": MessageLookupByLibrary.simpleMessage( + "Enter the email associated with your account and we\'ll send an email with password reset link"), + "passwordSuccessNotification": MessageLookupByLibrary.simpleMessage( + "Password successfully changed"), + "phone": MessageLookupByLibrary.simpleMessage("Phone"), + "postalCode": MessageLookupByLibrary.simpleMessage("Zip / Postal Code"), + "profileSuccessNotification": MessageLookupByLibrary.simpleMessage( + "Profile successfully updated"), + "requestPasswordReset": + MessageLookupByLibrary.simpleMessage("Request password reset"), + "stateOrProvince": + MessageLookupByLibrary.simpleMessage("State / Province"), + "systemAdministrator": + MessageLookupByLibrary.simpleMessage("System Administrator"), + "tenantAdministrator": + MessageLookupByLibrary.simpleMessage("Tenant Administrator"), + "title": MessageLookupByLibrary.simpleMessage("Title"), + "tryAgain": MessageLookupByLibrary.simpleMessage("Try Again"), + "type": MessageLookupByLibrary.simpleMessage("Type"), + "username": MessageLookupByLibrary.simpleMessage("username") + }; +} diff --git a/lib/generated/intl/messages_zh.dart b/lib/generated/intl/messages_zh.dart new file mode 100644 index 0000000..d022ffa --- /dev/null +++ b/lib/generated/intl/messages_zh.dart @@ -0,0 +1,107 @@ +// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart +// This is a library that provides messages for a zh locale. All the +// messages from the main program should be duplicated here with the same +// function name. + +// Ignore issues from commonly used lints in this file. +// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new +// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering +// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases +// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes +// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes + +import 'package:intl/intl.dart'; +import 'package:intl/message_lookup_by_library.dart'; + +final messages = new MessageLookup(); + +typedef String MessageIfAbsent(String messageStr, List args); + +class MessageLookup extends MessageLookupByLibrary { + String get localeName => 'zh'; + + final messages = _notInlinedMessages(_notInlinedMessages); + static Map _notInlinedMessages(_) => { + "No": MessageLookupByLibrary.simpleMessage("否"), + "OR": MessageLookupByLibrary.simpleMessage("或"), + "Yes": MessageLookupByLibrary.simpleMessage("是"), + "actionData": MessageLookupByLibrary.simpleMessage("动作数据"), + "active": MessageLookupByLibrary.simpleMessage("激活"), + "address": MessageLookupByLibrary.simpleMessage("地址"), + "address2": MessageLookupByLibrary.simpleMessage("地址 2"), + "alarmAcknowledgeText": + MessageLookupByLibrary.simpleMessage("你确定要确认告警吗?"), + "alarmAcknowledgeTitle": MessageLookupByLibrary.simpleMessage("确认告警"), + "alarmClearText": MessageLookupByLibrary.simpleMessage("你确定要清除告警吗?"), + "alarmClearTitle": MessageLookupByLibrary.simpleMessage("清除告警"), + "alarms": MessageLookupByLibrary.simpleMessage("告警"), + "allDevices": MessageLookupByLibrary.simpleMessage("所有设备"), + "appTitle": MessageLookupByLibrary.simpleMessage("Thingsboard"), + "assetName": MessageLookupByLibrary.simpleMessage("资产名"), + "assignedToCustomer": MessageLookupByLibrary.simpleMessage("分配给客户"), + "auditLogDetails": MessageLookupByLibrary.simpleMessage("审计日志详情"), + "auditLogs": MessageLookupByLibrary.simpleMessage("审计报告"), + "changePassword": MessageLookupByLibrary.simpleMessage("修改密码"), + "city": MessageLookupByLibrary.simpleMessage("城市"), + "country": MessageLookupByLibrary.simpleMessage("国家"), + "currentPassword": MessageLookupByLibrary.simpleMessage("当前密码"), + "currentPasswordRequireText": + MessageLookupByLibrary.simpleMessage("输入当前密码"), + "currentPasswordStar": MessageLookupByLibrary.simpleMessage("当前密码 *"), + "customer": MessageLookupByLibrary.simpleMessage("客户"), + "customers": MessageLookupByLibrary.simpleMessage("客户"), + "devices": MessageLookupByLibrary.simpleMessage("设备"), + "email": MessageLookupByLibrary.simpleMessage("Email"), + "emailInvalidText": MessageLookupByLibrary.simpleMessage("Email格式错误"), + "emailRequireText": MessageLookupByLibrary.simpleMessage("输入Email"), + "emailStar": MessageLookupByLibrary.simpleMessage("Email *"), + "entityType": MessageLookupByLibrary.simpleMessage("实体类型"), + "failureDetails": MessageLookupByLibrary.simpleMessage("失败详情"), + "firstName": MessageLookupByLibrary.simpleMessage("名"), + "firstNameUpper": MessageLookupByLibrary.simpleMessage("名"), + "home": MessageLookupByLibrary.simpleMessage("主页"), + "inactive": MessageLookupByLibrary.simpleMessage("失活"), + "label": MessageLookupByLibrary.simpleMessage("标签"), + "lastName": MessageLookupByLibrary.simpleMessage("姓"), + "lastNameUpper": MessageLookupByLibrary.simpleMessage("姓"), + "listIsEmptyText": MessageLookupByLibrary.simpleMessage("列表当前为空"), + "login": MessageLookupByLibrary.simpleMessage("登录"), + "loginNotification": MessageLookupByLibrary.simpleMessage("登录你的账号"), + "logoDefaultValue": + MessageLookupByLibrary.simpleMessage("Thingsboard Logo"), + "logout": MessageLookupByLibrary.simpleMessage("登出"), + "more": MessageLookupByLibrary.simpleMessage("更多"), + "newPassword": MessageLookupByLibrary.simpleMessage("新密码"), + "newPassword2": MessageLookupByLibrary.simpleMessage("新密码2"), + "newPassword2RequireText": + MessageLookupByLibrary.simpleMessage("再次输入新密码"), + "newPassword2Star": MessageLookupByLibrary.simpleMessage("再次输入新密码 *"), + "newPasswordRequireText": MessageLookupByLibrary.simpleMessage("输入新密码"), + "newPasswordStar": MessageLookupByLibrary.simpleMessage("新密码 *"), + "notImplemented": MessageLookupByLibrary.simpleMessage("未实现!"), + "password": MessageLookupByLibrary.simpleMessage("密码"), + "passwordErrorNotification": + MessageLookupByLibrary.simpleMessage("输入的密码必须相同"), + "passwordForgotText": MessageLookupByLibrary.simpleMessage("忘记密码?"), + "passwordRequireText": MessageLookupByLibrary.simpleMessage("输入密码"), + "passwordReset": MessageLookupByLibrary.simpleMessage("重置密码"), + "passwordResetLinkSuccessfullySentNotification": + MessageLookupByLibrary.simpleMessage("密码重置链接已发送"), + "passwordResetText": MessageLookupByLibrary.simpleMessage( + "输入和账号关联的Email,我们将发送一个密码重置链接到的Email"), + "passwordSuccessNotification": + MessageLookupByLibrary.simpleMessage("密码修改成功"), + "phone": MessageLookupByLibrary.simpleMessage("电话"), + "postalCode": MessageLookupByLibrary.simpleMessage("邮编"), + "profileSuccessNotification": + MessageLookupByLibrary.simpleMessage("配置更新成功"), + "requestPasswordReset": MessageLookupByLibrary.simpleMessage("要求重置密码"), + "stateOrProvince": MessageLookupByLibrary.simpleMessage("州 / 省"), + "systemAdministrator": MessageLookupByLibrary.simpleMessage("系统管理员"), + "tenantAdministrator": MessageLookupByLibrary.simpleMessage("租户管理员"), + "title": MessageLookupByLibrary.simpleMessage("标题"), + "tryAgain": MessageLookupByLibrary.simpleMessage("再试一次"), + "type": MessageLookupByLibrary.simpleMessage("类型"), + "username": MessageLookupByLibrary.simpleMessage("用户名") + }; +} diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart new file mode 100644 index 0000000..649bc3d --- /dev/null +++ b/lib/generated/l10n.dart @@ -0,0 +1,809 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'intl/messages_all.dart'; + +// ************************************************************************** +// Generator: Flutter Intl IDE plugin +// Made by Localizely +// ************************************************************************** + +// ignore_for_file: non_constant_identifier_names, lines_longer_than_80_chars +// ignore_for_file: join_return_with_assignment, prefer_final_in_for_each +// ignore_for_file: avoid_redundant_argument_values, avoid_escaping_inner_quotes + +class S { + S(); + + static S? _current; + + static S get current { + assert(_current != null, + 'No instance of S was loaded. Try to initialize the S delegate before accessing S.current.'); + return _current!; + } + + static const AppLocalizationDelegate delegate = AppLocalizationDelegate(); + + static Future load(Locale locale) { + final name = (locale.countryCode?.isEmpty ?? false) + ? locale.languageCode + : locale.toString(); + final localeName = Intl.canonicalizedLocale(name); + return initializeMessages(localeName).then((_) { + Intl.defaultLocale = localeName; + final instance = S(); + S._current = instance; + + return instance; + }); + } + + static S of(BuildContext context) { + final instance = S.maybeOf(context); + assert(instance != null, + 'No instance of S present in the widget tree. Did you add S.delegate in localizationsDelegates?'); + return instance!; + } + + static S? maybeOf(BuildContext context) { + return Localizations.of(context, S); + } + + /// `Thingsboard` + String get appTitle { + return Intl.message( + 'Thingsboard', + name: 'appTitle', + desc: '', + args: [], + ); + } + + /// `Home` + String get home { + return Intl.message( + 'Home', + name: 'home', + desc: '', + args: [], + ); + } + + /// `Alarms` + String get alarms { + return Intl.message( + 'Alarms', + name: 'alarms', + desc: '', + args: [], + ); + } + + /// `Devices` + String get devices { + return Intl.message( + 'Devices', + name: 'devices', + desc: '', + args: [], + ); + } + + /// `More` + String get more { + return Intl.message( + 'More', + name: 'more', + desc: '', + args: [], + ); + } + + /// `Customers` + String get customers { + return Intl.message( + 'Customers', + name: 'customers', + desc: '', + args: [], + ); + } + + /// `Assets` + String get assets { + return Intl.message( + 'Assets', + name: 'assets', + desc: '', + args: [], + ); + } + + /// `Audit Logs` + String get auditLogs { + return Intl.message( + 'Audit Logs', + name: 'auditLogs', + desc: '', + args: [], + ); + } + + /// `Log Out` + String get logout { + return Intl.message( + 'Log Out', + name: 'logout', + desc: '', + args: [], + ); + } + + /// `Log In` + String get login { + return Intl.message( + 'Log In', + name: 'login', + desc: '', + args: [], + ); + } + + /// `Thingsboard Logo` + String get logoDefaultValue { + return Intl.message( + 'Thingsboard Logo', + name: 'logoDefaultValue', + desc: '', + args: [], + ); + } + + /// `Login to your account` + String get loginNotification { + return Intl.message( + 'Login to your account', + name: 'loginNotification', + desc: '', + args: [], + ); + } + + /// `Email` + String get email { + return Intl.message( + 'Email', + name: 'email', + desc: '', + args: [], + ); + } + + /// `Email is required.` + String get emailRequireText { + return Intl.message( + 'Email is required.', + name: 'emailRequireText', + desc: '', + args: [], + ); + } + + /// `Invalid email format.` + String get emailInvalidText { + return Intl.message( + 'Invalid email format.', + name: 'emailInvalidText', + desc: '', + args: [], + ); + } + + /// `username` + String get username { + return Intl.message( + 'username', + name: 'username', + desc: '', + args: [], + ); + } + + /// `Password` + String get password { + return Intl.message( + 'Password', + name: 'password', + desc: '', + args: [], + ); + } + + /// `Password is required.` + String get passwordRequireText { + return Intl.message( + 'Password is required.', + name: 'passwordRequireText', + desc: '', + args: [], + ); + } + + /// `Forgot Password?` + String get passwordForgotText { + return Intl.message( + 'Forgot Password?', + name: 'passwordForgotText', + desc: '', + args: [], + ); + } + + /// `Reset password` + String get passwordReset { + return Intl.message( + 'Reset password', + name: 'passwordReset', + desc: '', + args: [], + ); + } + + /// `Enter the email associated with your account and we'll send an email with password reset link` + String get passwordResetText { + return Intl.message( + 'Enter the email associated with your account and we\'ll send an email with password reset link', + name: 'passwordResetText', + desc: '', + args: [], + ); + } + + /// `Request password reset` + String get requestPasswordReset { + return Intl.message( + 'Request password reset', + name: 'requestPasswordReset', + desc: '', + args: [], + ); + } + + /// `Password reset link was successfully sent!` + String get passwordResetLinkSuccessfullySentNotification { + return Intl.message( + 'Password reset link was successfully sent!', + name: 'passwordResetLinkSuccessfullySentNotification', + desc: '', + args: [], + ); + } + + /// `OR` + String get OR { + return Intl.message( + 'OR', + name: 'OR', + desc: '', + args: [], + ); + } + + /// `No` + String get No { + return Intl.message( + 'No', + name: 'No', + desc: '', + args: [], + ); + } + + /// `Yes` + String get Yes { + return Intl.message( + 'Yes', + name: 'Yes', + desc: '', + args: [], + ); + } + + /// `Title` + String get title { + return Intl.message( + 'Title', + name: 'title', + desc: '', + args: [], + ); + } + + /// `Country` + String get country { + return Intl.message( + 'Country', + name: 'country', + desc: '', + args: [], + ); + } + + /// `City` + String get city { + return Intl.message( + 'City', + name: 'city', + desc: '', + args: [], + ); + } + + /// `State / Province` + String get stateOrProvince { + return Intl.message( + 'State / Province', + name: 'stateOrProvince', + desc: '', + args: [], + ); + } + + /// `Zip / Postal Code` + String get postalCode { + return Intl.message( + 'Zip / Postal Code', + name: 'postalCode', + desc: '', + args: [], + ); + } + + /// `Address` + String get address { + return Intl.message( + 'Address', + name: 'address', + desc: '', + args: [], + ); + } + + /// `Address 2` + String get address2 { + return Intl.message( + 'Address 2', + name: 'address2', + desc: '', + args: [], + ); + } + + /// `Phone` + String get phone { + return Intl.message( + 'Phone', + name: 'phone', + desc: '', + args: [], + ); + } + + /// `Clear Alarm` + String get alarmClearTitle { + return Intl.message( + 'Clear Alarm', + name: 'alarmClearTitle', + desc: '', + args: [], + ); + } + + /// `Are you sure you want to clear Alarm?` + String get alarmClearText { + return Intl.message( + 'Are you sure you want to clear Alarm?', + name: 'alarmClearText', + desc: '', + args: [], + ); + } + + /// `Acknowledge Alarm` + String get alarmAcknowledgeTitle { + return Intl.message( + 'Acknowledge Alarm', + name: 'alarmAcknowledgeTitle', + desc: '', + args: [], + ); + } + + /// `Are you sure you want to acknowledge Alarm?` + String get alarmAcknowledgeText { + return Intl.message( + 'Are you sure you want to acknowledge Alarm?', + name: 'alarmAcknowledgeText', + desc: '', + args: [], + ); + } + + /// `Asset name` + String get assetName { + return Intl.message( + 'Asset name', + name: 'assetName', + desc: '', + args: [], + ); + } + + /// `Type` + String get type { + return Intl.message( + 'Type', + name: 'type', + desc: '', + args: [], + ); + } + + /// `Label` + String get label { + return Intl.message( + 'Label', + name: 'label', + desc: '', + args: [], + ); + } + + /// `Assigned to customer` + String get assignedToCustomer { + return Intl.message( + 'Assigned to customer', + name: 'assignedToCustomer', + desc: '', + args: [], + ); + } + + /// `Audit log details` + String get auditLogDetails { + return Intl.message( + 'Audit log details', + name: 'auditLogDetails', + desc: '', + args: [], + ); + } + + /// `Entity Type` + String get entityType { + return Intl.message( + 'Entity Type', + name: 'entityType', + desc: '', + args: [], + ); + } + + /// `Action data` + String get actionData { + return Intl.message( + 'Action data', + name: 'actionData', + desc: '', + args: [], + ); + } + + /// `Failure details` + String get failureDetails { + return Intl.message( + 'Failure details', + name: 'failureDetails', + desc: '', + args: [], + ); + } + + /// `All devices` + String get allDevices { + return Intl.message( + 'All devices', + name: 'allDevices', + desc: '', + args: [], + ); + } + + /// `Active` + String get active { + return Intl.message( + 'Active', + name: 'active', + desc: '', + args: [], + ); + } + + /// `Inactive` + String get inactive { + return Intl.message( + 'Inactive', + name: 'inactive', + desc: '', + args: [], + ); + } + + /// `System Administrator` + String get systemAdministrator { + return Intl.message( + 'System Administrator', + name: 'systemAdministrator', + desc: '', + args: [], + ); + } + + /// `Tenant Administrator` + String get tenantAdministrator { + return Intl.message( + 'Tenant Administrator', + name: 'tenantAdministrator', + desc: '', + args: [], + ); + } + + /// `Customer` + String get customer { + return Intl.message( + 'Customer', + name: 'customer', + desc: '', + args: [], + ); + } + + /// `Change Password` + String get changePassword { + return Intl.message( + 'Change Password', + name: 'changePassword', + desc: '', + args: [], + ); + } + + /// `currentPassword` + String get currentPassword { + return Intl.message( + 'currentPassword', + name: 'currentPassword', + desc: '', + args: [], + ); + } + + /// `Current password is required.` + String get currentPasswordRequireText { + return Intl.message( + 'Current password is required.', + name: 'currentPasswordRequireText', + desc: '', + args: [], + ); + } + + /// `Current password *` + String get currentPasswordStar { + return Intl.message( + 'Current password *', + name: 'currentPasswordStar', + desc: '', + args: [], + ); + } + + /// `newPassword` + String get newPassword { + return Intl.message( + 'newPassword', + name: 'newPassword', + desc: '', + args: [], + ); + } + + /// `New password is required.` + String get newPasswordRequireText { + return Intl.message( + 'New password is required.', + name: 'newPasswordRequireText', + desc: '', + args: [], + ); + } + + /// `New password *` + String get newPasswordStar { + return Intl.message( + 'New password *', + name: 'newPasswordStar', + desc: '', + args: [], + ); + } + + /// `newPassword2` + String get newPassword2 { + return Intl.message( + 'newPassword2', + name: 'newPassword2', + desc: '', + args: [], + ); + } + + /// `New password again is required.` + String get newPassword2RequireText { + return Intl.message( + 'New password again is required.', + name: 'newPassword2RequireText', + desc: '', + args: [], + ); + } + + /// `New password again *` + String get newPassword2Star { + return Intl.message( + 'New password again *', + name: 'newPassword2Star', + desc: '', + args: [], + ); + } + + /// `Entered passwords must be same!` + String get passwordErrorNotification { + return Intl.message( + 'Entered passwords must be same!', + name: 'passwordErrorNotification', + desc: '', + args: [], + ); + } + + /// `Email *` + String get emailStar { + return Intl.message( + 'Email *', + name: 'emailStar', + desc: '', + args: [], + ); + } + + /// `firstName` + String get firstName { + return Intl.message( + 'firstName', + name: 'firstName', + desc: '', + args: [], + ); + } + + /// `First Name` + String get firstNameUpper { + return Intl.message( + 'First Name', + name: 'firstNameUpper', + desc: '', + args: [], + ); + } + + /// `lastName` + String get lastName { + return Intl.message( + 'lastName', + name: 'lastName', + desc: '', + args: [], + ); + } + + /// `Last Name` + String get lastNameUpper { + return Intl.message( + 'Last Name', + name: 'lastNameUpper', + desc: '', + args: [], + ); + } + + /// `Profile successfully updated` + String get profileSuccessNotification { + return Intl.message( + 'Profile successfully updated', + name: 'profileSuccessNotification', + desc: '', + args: [], + ); + } + + /// `Password successfully changed` + String get passwordSuccessNotification { + return Intl.message( + 'Password successfully changed', + name: 'passwordSuccessNotification', + desc: '', + args: [], + ); + } + + /// `Not implemented!` + String get notImplemented { + return Intl.message( + 'Not implemented!', + name: 'notImplemented', + desc: '', + args: [], + ); + } + + /// `The list is currently empty.` + String get listIsEmptyText { + return Intl.message( + 'The list is currently empty.', + name: 'listIsEmptyText', + desc: '', + args: [], + ); + } + + /// `Try Again` + String get tryAgain { + return Intl.message( + 'Try Again', + name: 'tryAgain', + desc: '', + args: [], + ); + } +} + +class AppLocalizationDelegate extends LocalizationsDelegate { + const AppLocalizationDelegate(); + + List get supportedLocales { + return const [ + Locale.fromSubtags(languageCode: 'en'), + Locale.fromSubtags(languageCode: 'zh'), + ]; + } + + @override + bool isSupported(Locale locale) => _isSupported(locale); + @override + Future load(Locale locale) => S.load(locale); + @override + bool shouldReload(AppLocalizationDelegate old) => false; + + bool _isSupported(Locale locale) { + for (var supportedLocale in supportedLocales) { + if (supportedLocale.languageCode == locale.languageCode) { + return true; + } + } + return false; + } +} diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb new file mode 100644 index 0000000..81ce3ce --- /dev/null +++ b/lib/l10n/intl_en.arb @@ -0,0 +1,91 @@ +{ + "appTitle": "Thingsboard", + + "home": "Home", + "alarms": "Alarms", + "devices": "Devices", + "more": "More", + + "customers": "Customers", + "assets": "Assets", + "auditLogs": "Audit Logs", + "logout": "Log Out", + "login": "Log In", + + "logoDefaultValue": "Thingsboard Logo", + "loginNotification": "Login to your account", + "email": "Email", + "emailRequireText": "Email is required.", + "emailInvalidText": "Invalid email format.", + "username": "username", + "password": "Password", + "passwordRequireText": "Password is required.", + "passwordForgotText": "Forgot Password?", + "passwordReset": "Reset password", + "passwordResetText": "Enter the email associated with your account and we'll send an email with password reset link", + "requestPasswordReset": "Request password reset", + "passwordResetLinkSuccessfullySentNotification": "Password reset link was successfully sent!", + + "OR": "OR", + "No": "No", + "Yes": "Yes", + + "title": "Title", + "country": "Country", + "city": "City", + "stateOrProvince": "State / Province", + "postalCode": "Zip / Postal Code", + "address": "Address", + "address2": "Address 2", + "phone": "Phone", + + "alarmClearTitle": "Clear Alarm", + "alarmClearText": "Are you sure you want to clear Alarm?", + + "alarmAcknowledgeTitle": "Acknowledge Alarm", + "alarmAcknowledgeText": "Are you sure you want to acknowledge Alarm?", + + "assetName": "Asset name", + "type": "Type", + "label": "Label", + "assignedToCustomer": "Assigned to customer", + + "auditLogDetails": "Audit log details", + "entityType": "Entity Type", + "actionData": "Action data", + "failureDetails": "Failure details", + + "allDevices": "All devices", + "active": "Active", + "inactive": "Inactive", + + "systemAdministrator": "System Administrator", + "tenantAdministrator": "Tenant Administrator", + "customer": "Customer", + + "changePassword": "Change Password", + "currentPassword": "currentPassword", + "currentPasswordRequireText": "Current password is required.", + "currentPasswordStar": "Current password *", + "newPassword": "newPassword", + "newPasswordRequireText": "New password is required.", + "newPasswordStar": "New password *", + "newPassword2": "newPassword2", + "newPassword2RequireText": "New password again is required.", + "newPassword2Star": "New password again *", + "passwordErrorNotification": "Entered passwords must be same!", + + "emailStar": "Email *", + "firstName": "firstName", + "firstNameUpper": "First Name", + "lastName": "lastName", + "lastNameUpper": "Last Name", + "profileSuccessNotification": "Profile successfully updated", + "passwordSuccessNotification": "Password successfully changed", + + "notImplemented": "Not implemented!", + + "listIsEmptyText": "The list is currently empty.", + "tryAgain": "Try Again" + +} \ No newline at end of file diff --git a/lib/l10n/intl_zh.arb b/lib/l10n/intl_zh.arb new file mode 100644 index 0000000..b338e84 --- /dev/null +++ b/lib/l10n/intl_zh.arb @@ -0,0 +1,90 @@ +{ + "appTitle": "Thingsboard", + + "home": "主页", + "alarms": "告警", + "devices": "设备", + "more": "更多", + + "customers": "客户", + "asserts": "资产", + "auditLogs": "审计报告", + "logout": "登出", + "login": "登录", + + "logoDefaultValue": "Thingsboard Logo", + "loginNotification": "登录你的账号", + "email": "Email", + "emailRequireText": "输入Email", + "emailInvalidText": "Email格式错误", + "username": "用户名", + "password": "密码", + "passwordRequireText": "输入密码", + "passwordForgotText": "忘记密码?", + "passwordReset": "重置密码", + "passwordResetText": "输入和账号关联的Email,我们将发送一个密码重置链接到的Email", + "requestPasswordReset": "要求重置密码", + "passwordResetLinkSuccessfullySentNotification": "密码重置链接已发送", + + "OR": "或", + "No": "否", + "Yes": "是", + + "title": "标题", + "country": "国家", + "city": "城市", + "stateOrProvince": "州 / 省", + "postalCode": "邮编", + "address": "地址", + "address2": "地址 2", + "phone": "电话", + + "alarmClearTitle": "清除告警", + "alarmClearText": "你确定要清除告警吗?", + + "alarmAcknowledgeTitle": "确认告警", + "alarmAcknowledgeText": "你确定要确认告警吗?", + + "assetName": "资产名", + "type": "类型", + "label": "标签", + "assignedToCustomer": "分配给客户", + + "auditLogDetails": "审计日志详情", + "entityType": "实体类型", + "actionData": "动作数据", + "failureDetails": "失败详情", + + "allDevices": "所有设备", + "active": "激活", + "inactive": "失活", + + "systemAdministrator": "系统管理员", + "tenantAdministrator": "租户管理员", + "customer": "客户", + + "changePassword": "修改密码", + "currentPassword": "当前密码", + "currentPasswordRequireText": "输入当前密码", + "currentPasswordStar": "当前密码 *", + "newPassword": "新密码", + "newPasswordRequireText": "输入新密码", + "newPasswordStar": "新密码 *", + "newPassword2": "新密码2", + "newPassword2RequireText": "再次输入新密码", + "newPassword2Star": "再次输入新密码 *", + "passwordErrorNotification": "输入的密码必须相同", + + "emailStar": "Email *", + "firstName": "名", + "firstNameUpper": "名", + "lastName": "姓", + "lastNameUpper": "姓", + "profileSuccessNotification": "配置更新成功", + "passwordSuccessNotification": "密码修改成功", + + "notImplemented": "未实现!", + + "listIsEmptyText": "列表当前为空", + "tryAgain": "再试一次" +} \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index f5710e3..45b7d66 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,3 +1,4 @@ +import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:universal_platform/universal_platform.dart'; import 'package:flutter/material.dart'; @@ -9,6 +10,7 @@ import 'package:thingsboard_app/modules/dashboard/main_dashboard_page.dart'; import 'package:thingsboard_app/widgets/two_page_view.dart'; import 'config/themes/tb_theme.dart'; +import 'generated/l10n.dart'; final appRouter = ThingsboardAppRouter(); @@ -131,14 +133,28 @@ class ThingsboardAppState extends State statusBarColor: Colors.white, systemNavigationBarIconBrightness: Brightness.light)); return MaterialApp( - title: 'ThingsBoard', + localizationsDelegates: [ + S.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: S.delegate.supportedLocales, + onGenerateTitle: (BuildContext context) => S.of(context).appTitle, themeMode: ThemeMode.light, home: TwoPageView( controller: _mainPageViewController, first: MaterialApp( key: mainAppKey, scaffoldMessengerKey: appRouter.tbContext.messengerKey, - title: 'ThingsBoard', + localizationsDelegates: [ + S.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: S.delegate.supportedLocales, + onGenerateTitle: (BuildContext context) => S.of(context).appTitle, theme: tbTheme, themeMode: ThemeMode.light, darkTheme: tbDarkTheme, @@ -148,7 +164,14 @@ class ThingsboardAppState extends State second: MaterialApp( key: dashboardKey, // scaffoldMessengerKey: appRouter.tbContext.messengerKey, - title: 'ThingsBoard', + localizationsDelegates: [ + S.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: S.delegate.supportedLocales, + onGenerateTitle: (BuildContext context) => S.of(context).appTitle, theme: tbTheme, themeMode: ThemeMode.light, darkTheme: tbDarkTheme, diff --git a/lib/modules/alarm/alarms_base.dart b/lib/modules/alarm/alarms_base.dart index 3192696..ea452cf 100644 --- a/lib/modules/alarm/alarms_base.dart +++ b/lib/modules/alarm/alarms_base.dart @@ -4,6 +4,7 @@ import 'package:intl/intl.dart'; import 'package:thingsboard_app/core/context/tb_context.dart'; import 'package:thingsboard_app/core/context/tb_context_widget.dart'; import 'package:thingsboard_app/core/entity/entities_base.dart'; +import 'package:thingsboard_app/generated/l10n.dart'; import 'package:thingsboard_app/utils/utils.dart'; import 'package:thingsboard_client/thingsboard_client.dart'; @@ -249,7 +250,8 @@ class _AlarmCardState extends TbContextState { child: IconButton( icon: Icon(Icons.done, size: 18), padding: EdgeInsets.all(7.0), - onPressed: () => _ackAlarm(alarm))), + onPressed: () => + _ackAlarm(alarm, context))), if ([ AlarmStatus.ACTIVE_UNACK, AlarmStatus.ACTIVE_ACK @@ -262,7 +264,8 @@ class _AlarmCardState extends TbContextState { child: IconButton( icon: Icon(Icons.clear, size: 18), padding: EdgeInsets.all(7.0), - onPressed: () => _clearAlarm(alarm))) + onPressed: () => + _clearAlarm(alarm, context))) ]) ], ), @@ -277,12 +280,12 @@ class _AlarmCardState extends TbContextState { } } - _clearAlarm(AlarmInfo alarm) async { + _clearAlarm(AlarmInfo alarm, BuildContext context) async { var res = await confirm( - title: 'Clear Alarm', - message: 'Are you sure you want to clear Alarm?', - cancel: 'No', - ok: 'Yes'); + title: '${S.of(context).alarmClearTitle}', + message: '${S.of(context).alarmClearText}', + cancel: '${S.of(context).No}', + ok: '${S.of(context).Yes}'); if (res != null && res) { setState(() { loading = true; @@ -297,12 +300,12 @@ class _AlarmCardState extends TbContextState { } } - _ackAlarm(AlarmInfo alarm) async { + _ackAlarm(AlarmInfo alarm, BuildContext context) async { var res = await confirm( - title: 'Acknowledge Alarm', - message: 'Are you sure you want to acknowledge Alarm?', - cancel: 'No', - ok: 'Yes'); + title: '${S.of(context).alarmAcknowledgeTitle}', + message: '${S.of(context).alarmAcknowledgeText}', + cancel: '${S.of(context).No}', + ok: '${S.of(context).Yes}'); if (res != null && res) { setState(() { loading = true; diff --git a/lib/modules/asset/asset_details_page.dart b/lib/modules/asset/asset_details_page.dart index 29055f1..eca8f78 100644 --- a/lib/modules/asset/asset_details_page.dart +++ b/lib/modules/asset/asset_details_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:thingsboard_app/core/context/tb_context.dart'; import 'package:thingsboard_app/core/entity/entity_details_page.dart'; +import 'package:thingsboard_app/generated/l10n.dart'; import 'package:thingsboard_client/thingsboard_client.dart'; class AssetDetailsPage extends EntityDetailsPage { @@ -24,16 +25,17 @@ class AssetDetailsPage extends EntityDetailsPage { crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.max, children: [ - Text('Asset name', style: labelTextStyle), + Text('${S.of(context).assetName}', style: labelTextStyle), Text(asset.name, style: valueTextStyle), SizedBox(height: 16), - Text('Type', style: labelTextStyle), + Text('${S.of(context).type}', style: labelTextStyle), Text(asset.type, style: valueTextStyle), SizedBox(height: 16), - Text('Label', style: labelTextStyle), + Text('${S.of(context).label}', style: labelTextStyle), Text(asset.label ?? '', style: valueTextStyle), SizedBox(height: 16), - Text('Assigned to customer', style: labelTextStyle), + Text('${S.of(context).assignedToCustomer}', + style: labelTextStyle), Text(asset.customerTitle ?? '', style: valueTextStyle), ])); } diff --git a/lib/modules/audit_log/audit_log_details_page.dart b/lib/modules/audit_log/audit_log_details_page.dart index a25b77e..b0f6f70 100644 --- a/lib/modules/audit_log/audit_log_details_page.dart +++ b/lib/modules/audit_log/audit_log_details_page.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:thingsboard_app/core/context/tb_context.dart'; import 'package:thingsboard_app/core/context/tb_context_widget.dart'; import 'package:thingsboard_app/core/entity/entities_base.dart'; +import 'package:thingsboard_app/generated/l10n.dart'; import 'package:thingsboard_app/modules/audit_log/audit_logs_base.dart'; import 'package:thingsboard_app/widgets/tb_app_bar.dart'; import 'package:thingsboard_client/thingsboard_client.dart'; @@ -39,7 +40,7 @@ class _AuditLogDetailsPageState extends TbContextState { fontWeight: FontWeight.w500, fontSize: 16, height: 20 / 16)), - Text('Audit log details', + Text('${S.of(context).auditLogDetails}', style: TextStyle( color: Theme.of(context) .primaryTextTheme @@ -56,24 +57,24 @@ class _AuditLogDetailsPageState extends TbContextState { crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.max, children: [ - Text('Entity Type', style: labelTextStyle), + Text('${S.of(context).entityType}', style: labelTextStyle), Text(entityTypeTranslations[widget.auditLog.entityId.entityType]!, style: valueTextStyle), SizedBox(height: 16), - Text('Type', style: labelTextStyle), + Text('${S.of(context).type}', style: labelTextStyle), Text(actionTypeTranslations[widget.auditLog.actionType]!, style: valueTextStyle), SizedBox(height: 16), Flexible( fit: FlexFit.loose, - child: buildBorderedText('Action data', + child: buildBorderedText('${S.of(context).actionData}', encoder.convert(widget.auditLog.actionData))), if (widget.auditLog.actionStatus == ActionStatus.FAILURE) SizedBox(height: 16), if (widget.auditLog.actionStatus == ActionStatus.FAILURE) Flexible( fit: FlexFit.loose, - child: buildBorderedText('Failure details', + child: buildBorderedText('${S.of(context).failureDetails}', widget.auditLog.actionFailureDetails!)) ]), ), diff --git a/lib/modules/device/device_profiles_base.dart b/lib/modules/device/device_profiles_base.dart index 5fcebae..5670648 100644 --- a/lib/modules/device/device_profiles_base.dart +++ b/lib/modules/device/device_profiles_base.dart @@ -7,6 +7,7 @@ import 'package:thingsboard_app/constants/assets_path.dart'; import 'package:thingsboard_app/core/context/tb_context.dart'; import 'package:thingsboard_app/core/context/tb_context_widget.dart'; import 'package:thingsboard_app/core/entity/entities_base.dart'; +import 'package:thingsboard_app/generated/l10n.dart'; import 'package:thingsboard_app/utils/services/device_profile_cache.dart'; import 'package:thingsboard_app/utils/services/entity_query_api.dart'; import 'package:thingsboard_app/utils/utils.dart'; @@ -135,7 +136,7 @@ class _AllDevicesCardState extends TbContextState { mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text('All devices', + Text('${S.of(context).allDevices}', style: TextStyle( fontWeight: FontWeight.w500, fontSize: 14, @@ -414,7 +415,10 @@ Widget _buildDeviceCount(BuildContext context, bool active, int count) { ], ), SizedBox(width: 8.67), - Text(active ? 'Active' : 'Inactive', + Text( + active + ? '${S.of(context).active}' + : '${S.of(context).inactive}', style: TextStyle( fontSize: 12, fontWeight: FontWeight.w500, diff --git a/lib/modules/device/devices_base.dart b/lib/modules/device/devices_base.dart index 03f7090..e08ab0e 100644 --- a/lib/modules/device/devices_base.dart +++ b/lib/modules/device/devices_base.dart @@ -7,6 +7,7 @@ import 'package:thingsboard_app/constants/assets_path.dart'; import 'package:thingsboard_app/core/context/tb_context.dart'; import 'package:thingsboard_app/core/context/tb_context_widget.dart'; import 'package:thingsboard_app/core/entity/entities_base.dart'; +import 'package:thingsboard_app/generated/l10n.dart'; import 'package:thingsboard_app/utils/services/device_profile_cache.dart'; import 'package:thingsboard_app/utils/services/entity_query_api.dart'; import 'package:thingsboard_app/utils/utils.dart'; @@ -266,8 +267,8 @@ class _DeviceCardState extends TbContextState { widget.device.attribute( 'active') == 'true' - ? 'Active' - : 'Inactive', + ? '${S.of(context).active}' + : '${S.of(context).inactive}', style: TextStyle( color: widget.device .attribute( diff --git a/lib/modules/device/devices_list_page.dart b/lib/modules/device/devices_list_page.dart index 16d969a..caf1061 100644 --- a/lib/modules/device/devices_list_page.dart +++ b/lib/modules/device/devices_list_page.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:thingsboard_app/core/context/tb_context.dart'; import 'package:thingsboard_app/core/context/tb_context_widget.dart'; +import 'package:thingsboard_app/generated/l10n.dart'; import 'package:thingsboard_app/modules/device/devices_base.dart'; import 'package:thingsboard_app/modules/device/devices_list.dart'; import 'package:thingsboard_app/widgets/tb_app_bar.dart'; @@ -41,11 +42,14 @@ class _DevicesListPageState extends TbPageState { _deviceQueryController.onSearchText(searchText), ); } else { - String titleText = - widget.deviceType != null ? widget.deviceType! : 'All devices'; + String titleText = widget.deviceType != null + ? widget.deviceType! + : '${S.of(context).allDevices}'; String? subTitleText; if (widget.active != null) { - subTitleText = widget.active == true ? 'Active' : 'Inactive'; + subTitleText = widget.active == true + ? '${S.of(context).active}' + : '${S.of(context).inactive}'; } Column title = Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ diff --git a/lib/modules/main/main_page.dart b/lib/modules/main/main_page.dart index 1766444..f2761dd 100644 --- a/lib/modules/main/main_page.dart +++ b/lib/modules/main/main_page.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:thingsboard_app/core/context/tb_context.dart'; import 'package:thingsboard_app/core/context/tb_context_widget.dart'; +import 'package:thingsboard_app/generated/l10n.dart'; import 'package:thingsboard_app/modules/alarm/alarms_page.dart'; import 'package:thingsboard_app/modules/device/devices_main_page.dart'; import 'package:thingsboard_app/modules/home/home_page.dart'; @@ -9,7 +10,7 @@ import 'package:thingsboard_client/thingsboard_client.dart'; class TbMainNavigationItem { final Widget page; - final String title; + String title; final Icon icon; final String path; @@ -80,6 +81,26 @@ class TbMainNavigationItem { return []; } } + + static void changeItemsTitleIntl( + List items, BuildContext context) { + for (var item in items) { + switch (item.path) { + case '/home': + item.title = '${S.of(context).home}'; + break; + case '/alarms': + item.title = '${S.of(context).alarms}'; + break; + case '/devices': + item.title = '${S.of(context).devices}'; + break; + case '/more': + item.title = '${S.of(context).more}'; + break; + } + } + } } class MainPage extends TbPageWidget { @@ -129,6 +150,7 @@ class _MainPageState extends TbPageState @override Widget build(BuildContext context) { + TbMainNavigationItem.changeItemsTitleIntl(_tabItems, context); return WillPopScope( onWillPop: () async { if (_tabController.index > 0) { diff --git a/lib/modules/more/more_page.dart b/lib/modules/more/more_page.dart index 6f9ae1d..3f90eb7 100644 --- a/lib/modules/more/more_page.dart +++ b/lib/modules/more/more_page.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:thingsboard_app/core/context/tb_context.dart'; import 'package:thingsboard_app/core/context/tb_context_widget.dart'; +import 'package:thingsboard_app/generated/l10n.dart'; import 'package:thingsboard_client/thingsboard_client.dart'; class MorePage extends TbContextWidget { @@ -42,7 +43,7 @@ class _MorePageState extends TbContextState { fontSize: 20, height: 23 / 20)), SizedBox(height: 2), - Text(_getAuthorityName(), + Text(_getAuthorityName(context), style: TextStyle( color: Color(0xFFAFAFAF), fontWeight: FontWeight.normal, @@ -65,7 +66,7 @@ class _MorePageState extends TbContextState { child: Row(mainAxisSize: MainAxisSize.max, children: [ Icon(Icons.logout, color: Color(0xFFE04B2F)), SizedBox(width: 34), - Text('Log out', + Text('${S.of(context).logout}', style: TextStyle( color: Color(0xFFE04B2F), fontStyle: FontStyle.normal, @@ -83,7 +84,8 @@ class _MorePageState extends TbContextState { } Widget buildMoreMenuItems(BuildContext context) { - List items = MoreMenuItem.getItems(tbContext).map((menuItem) { + List items = + MoreMenuItem.getItems(tbContext, context).map((menuItem) { return GestureDetector( behavior: HitTestBehavior.opaque, child: Container( @@ -130,20 +132,20 @@ class _MorePageState extends TbContextState { return name; } - String _getAuthorityName() { + String _getAuthorityName(BuildContext context) { var user = tbContext.userDetails; var name = ''; if (user != null) { var authority = user.authority; switch (authority) { case Authority.SYS_ADMIN: - name = 'System Administrator'; + name = '${S.of(context).systemAdministrator}'; break; case Authority.TENANT_ADMIN: - name = 'Tenant Administrator'; + name = '${S.of(context).tenantAdministrator}'; break; case Authority.CUSTOMER_USER: - name = 'Customer'; + name = '${S.of(context).customer}'; break; default: break; @@ -160,7 +162,8 @@ class MoreMenuItem { MoreMenuItem({required this.title, required this.icon, required this.path}); - static List getItems(TbContext tbContext) { + static List getItems( + TbContext tbContext, BuildContext context) { if (tbContext.isAuthenticated) { List items = []; switch (tbContext.tbClient.getAuthUser()!.authority) { @@ -169,19 +172,25 @@ class MoreMenuItem { case Authority.TENANT_ADMIN: items.addAll([ MoreMenuItem( - title: 'Customers', + title: '${S.of(context).customers}', icon: Icons.supervisor_account, path: '/customers'), - MoreMenuItem(title: 'Assets', icon: Icons.domain, path: '/assets'), MoreMenuItem( - title: 'Audit Logs', + title: '${S.of(context).assets}', + icon: Icons.domain, + path: '/assets'), + MoreMenuItem( + title: '${S.of(context).auditLogs}', icon: Icons.track_changes, path: '/auditLogs') ]); break; case Authority.CUSTOMER_USER: items.addAll([ - MoreMenuItem(title: 'Assets', icon: Icons.domain, path: '/assets') + MoreMenuItem( + title: '${S.of(context).assets}', + icon: Icons.domain, + path: '/assets') ]); break; case Authority.REFRESH_TOKEN: diff --git a/lib/modules/profile/change_password_page.dart b/lib/modules/profile/change_password_page.dart index 4da2b42..cba6f88 100644 --- a/lib/modules/profile/change_password_page.dart +++ b/lib/modules/profile/change_password_page.dart @@ -3,6 +3,7 @@ import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:thingsboard_app/core/context/tb_context.dart'; import 'package:thingsboard_app/core/context/tb_context_widget.dart'; +import 'package:thingsboard_app/generated/l10n.dart'; import 'package:thingsboard_app/widgets/tb_app_bar.dart'; import 'package:thingsboard_app/widgets/tb_progress_indicator.dart'; @@ -28,7 +29,7 @@ class _ChangePasswordPageState extends TbContextState { backgroundColor: Colors.white, appBar: TbAppBar( tbContext, - title: const Text('Change Password'), + title: Text('${S.of(context).changePassword}'), ), body: Stack( children: [ @@ -54,7 +55,7 @@ class _ChangePasswordPageState extends TbContextState { validator: FormBuilderValidators.compose([ FormBuilderValidators.required( errorText: - 'Current password is required.') + '${S.of(context).currentPasswordRequireText}') ]), decoration: InputDecoration( suffixIcon: IconButton( @@ -68,7 +69,8 @@ class _ChangePasswordPageState extends TbContextState { }, ), border: OutlineInputBorder(), - labelText: 'Current password *'), + labelText: + '${S.of(context).currentPasswordStar}'), ); }), SizedBox(height: 24), @@ -81,7 +83,8 @@ class _ChangePasswordPageState extends TbContextState { obscureText: !showPassword, validator: FormBuilderValidators.compose([ FormBuilderValidators.required( - errorText: 'New password is required.') + errorText: + '${S.of(context).newPasswordRequireText}') ]), decoration: InputDecoration( suffixIcon: IconButton( @@ -94,7 +97,8 @@ class _ChangePasswordPageState extends TbContextState { }, ), border: OutlineInputBorder(), - labelText: 'New password *'), + labelText: + '${S.of(context).newPasswordStar}'), ); }), SizedBox(height: 24), @@ -108,7 +112,7 @@ class _ChangePasswordPageState extends TbContextState { validator: FormBuilderValidators.compose([ FormBuilderValidators.required( errorText: - 'New password again is required.') + '${S.of(context).newPassword2RequireText}') ]), decoration: InputDecoration( suffixIcon: IconButton( @@ -121,7 +125,8 @@ class _ChangePasswordPageState extends TbContextState { }, ), border: OutlineInputBorder(), - labelText: 'New password again *'), + labelText: + '${S.of(context).newPassword2Star}'), ); }), SizedBox(height: 24), @@ -132,7 +137,9 @@ class _ChangePasswordPageState extends TbContextState { onPressed: () { _changePassword(); }, - child: Center(child: Text('Change Password'))) + child: Center( + child: + Text('${S.of(context).changePassword}'))) ]), ))), ), @@ -161,7 +168,7 @@ class _ChangePasswordPageState extends TbContextState { String newPassword = formValue['newPassword']; String newPassword2 = formValue['newPassword2']; if (newPassword != newPassword2) { - showErrorNotification('Entered passwords must be same!'); + showErrorNotification('${S.of(context).passwordErrorNotification}'); } else { _isLoadingNotifier.value = true; try { diff --git a/lib/modules/profile/profile_page.dart b/lib/modules/profile/profile_page.dart index 6ae0a6b..ddd2393 100644 --- a/lib/modules/profile/profile_page.dart +++ b/lib/modules/profile/profile_page.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:thingsboard_app/generated/l10n.dart'; import 'package:thingsboard_app/modules/profile/change_password_page.dart'; import 'package:thingsboard_app/widgets/tb_app_bar.dart'; @@ -76,27 +77,29 @@ class _ProfilePageState extends TbPageState { name: 'email', validator: FormBuilderValidators.compose([ FormBuilderValidators.required( - errorText: 'Email is required.'), + errorText: + '${S.of(context).emailRequireText}'), FormBuilderValidators.email( - errorText: 'Invalid email format.') + errorText: + '${S.of(context).emailInvalidText}') ]), decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'Email *'), + labelText: '${S.of(context).emailStar}'), ), SizedBox(height: 24), FormBuilderTextField( name: 'firstName', decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'First Name'), + labelText: '${S.of(context).firstNameUpper}'), ), SizedBox(height: 24), FormBuilderTextField( name: 'lastName', decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'Last Name'), + labelText: '${S.of(context).lastNameUpper}'), ), SizedBox(height: 24), OutlinedButton( @@ -106,7 +109,9 @@ class _ProfilePageState extends TbPageState { onPressed: () { _changePassword(); }, - child: Center(child: Text('Change Password'))) + child: Center( + child: + Text('${S.of(context).changePassword}'))) ]), ))), ), @@ -156,7 +161,9 @@ class _ProfilePageState extends TbPageState { _setUser(); await Future.delayed(Duration(milliseconds: 300)); _isLoadingNotifier.value = false; - showSuccessNotification('Profile successfully updated', + showSuccessNotification('${S.of(context).profileSuccessNotification}', + duration: Duration(milliseconds: 1500)); + showSuccessNotification('${S.of(context).profileSuccessNotification}', duration: Duration(milliseconds: 1500)); } } @@ -166,7 +173,7 @@ class _ProfilePageState extends TbPageState { var res = await tbContext .showFullScreenDialog(new ChangePasswordPage(tbContext)); if (res == true) { - showSuccessNotification('Password successfully changed', + showSuccessNotification('${S.of(context).passwordSuccessNotification}', duration: Duration(milliseconds: 1500)); } } diff --git a/pubspec.lock b/pubspec.lock index 7bb17d4..7a5b615 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -191,7 +191,7 @@ packages: source: hosted version: "0.10.0" flutter_localizations: - dependency: transitive + dependency: "direct main" description: flutter source: sdk version: "0.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index 8ffc40b..f511a6c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -48,6 +48,8 @@ dependencies: universal_html: ^2.0.8 universal_platform: ^1.0.0+1 preload_page_view: ^0.1.6 + flutter_localizations: + sdk: flutter dev_dependencies: flutter_test: @@ -90,3 +92,5 @@ flutter_icons: ios: true remove_alpha_ios: true image_path: "assets/images/thingsboard.png" +flutter_intl: + enabled: true