diff --git a/lib/core/auth/login/login_page.dart b/lib/core/auth/login/login_page.dart index 7ea39da..a55715c 100644 --- a/lib/core/auth/login/login_page.dart +++ b/lib/core/auth/login/login_page.dart @@ -10,6 +10,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'; @@ -73,14 +74,14 @@ class _LoginPageState extends TbPageState { SvgPicture.asset(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', + '${S.of(context).loginNotification}', style: TextStyle( fontWeight: FontWeight.bold, fontSize: 28, @@ -98,7 +99,7 @@ class _LoginPageState extends TbPageState { Flexible(child: Divider()), Padding( padding: EdgeInsets.symmetric(horizontal: 16), - child: Text('OR'), + child: Text('${S.of(context).OR}'), ), Flexible(child: Divider()) ], @@ -113,12 +114,12 @@ class _LoginPageState extends TbPageState { FormBuilderTextField( name: 'username', validator: FormBuilderValidators.compose([ - FormBuilderValidators.required(context, errorText: 'Email is required.'), - FormBuilderValidators.email(context, errorText: 'Invalid email format.') + FormBuilderValidators.required(context, errorText: '${S.of(context).emailRequireText}'), + FormBuilderValidators.email(context, errorText: '${S.of(context).emailInvalidText}') ]), decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'Email' + labelText: '${S.of(context).email}' ), ), SizedBox(height: 28), @@ -129,7 +130,7 @@ class _LoginPageState extends TbPageState { name: 'password', obscureText: !showPassword, validator: FormBuilderValidators.compose([ - FormBuilderValidators.required(context, errorText: 'Password is required.') + FormBuilderValidators.required(context, errorText: '${S.of(context).passwordRequireText}') ]), decoration: InputDecoration( suffixIcon: IconButton( @@ -139,7 +140,7 @@ class _LoginPageState extends TbPageState { }, ), border: OutlineInputBorder(), - labelText: 'Password' + labelText: '${S.of(context).password}' ), ); } @@ -155,7 +156,7 @@ class _LoginPageState extends TbPageState { _forgotPassword(); }, child: Text( - 'Forgot Password?', + '${S.of(context).passwordForgotText}', style: TextStyle(color: Theme.of(context).colorScheme.primary, letterSpacing: 1, fontSize: 12, @@ -166,7 +167,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)), onPressed: () { _login(); diff --git a/lib/core/auth/login/reset_password_request_page.dart b/lib/core/auth/login/reset_password_request_page.dart index 218935e..fe1074c 100644 --- a/lib/core/auth/login/reset_password_request_page.dart +++ b/lib/core/auth/login/reset_password_request_page.dart @@ -5,6 +5,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'; @@ -33,7 +34,7 @@ class _ResetPasswordRequestPageState extends TbPageState 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; @@ -352,7 +353,7 @@ abstract class BaseEntitiesState extends TbContextState pagingController.refresh(), ); } @@ -407,8 +408,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..270596a --- /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("Athings"), + "asserts": MessageLookupByLibrary.simpleMessage("Asserts"), + "assetName": MessageLookupByLibrary.simpleMessage("Asset name"), + "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("Athings 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..297c98d --- /dev/null +++ b/lib/generated/intl/messages_zh.dart @@ -0,0 +1,108 @@ +// 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("Athings"), + "asserts": MessageLookupByLibrary.simpleMessage("资产"), + "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("Athings 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..18317a6 --- /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); + } + + /// `Athings` + String get appTitle { + return Intl.message( + 'Athings', + 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: [], + ); + } + + /// `Asserts` + String get asserts { + return Intl.message( + 'Asserts', + name: 'asserts', + 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: [], + ); + } + + /// `Athings Logo` + String get logoDefaultValue { + return Intl.message( + 'Athings 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..7fa29cc --- /dev/null +++ b/lib/l10n/intl_en.arb @@ -0,0 +1,91 @@ +{ + "appTitle": "Thingsboard", + + "home": "Home", + "alarms": "Alarms", + "devices": "Devices", + "more": "More", + + "customers": "Customers", + "asserts": "Asserts", + "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 3cce13c..ee60f99 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/foundation.dart'; @@ -10,6 +11,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(); @@ -130,14 +132,30 @@ class ThingsboardAppState extends State with TickerProviderState 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, @@ -147,7 +165,15 @@ class ThingsboardAppState extends State with TickerProviderState 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 5471cad..21549b5 100644 --- a/lib/modules/alarm/alarms_base.dart +++ b/lib/modules/alarm/alarms_base.dart @@ -5,6 +5,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'; @@ -238,7 +239,7 @@ class _AlarmCardState extends TbContextState { CircleAvatar( radius: 16, backgroundColor: Color(0xffF0F4F9), - child: IconButton(icon: Icon(Icons.done, size: 18), padding: EdgeInsets.all(7.0), onPressed: () => _ackAlarm(alarm)) + child: IconButton(icon: Icon(Icons.done, size: 18), padding: EdgeInsets.all(7.0), onPressed: () => _ackAlarm(alarm,context)) ), if ([AlarmStatus.ACTIVE_UNACK, AlarmStatus.ACTIVE_ACK].contains(alarm.status)) Row( @@ -247,7 +248,7 @@ class _AlarmCardState extends TbContextState { CircleAvatar( radius: 16, backgroundColor: Color(0xffF0F4F9), - child: IconButton(icon: Icon(Icons.clear, size: 18), padding: EdgeInsets.all(7.0), onPressed: () => _clearAlarm(alarm)) + child: IconButton(icon: Icon(Icons.clear, size: 18), padding: EdgeInsets.all(7.0), onPressed: () => _clearAlarm(alarm,context)) ) ] ) @@ -267,8 +268,8 @@ class _AlarmCardState extends TbContextState { } } - _clearAlarm(AlarmInfo alarm) async { - var res = await confirm(title: 'Clear Alarm', message: 'Are you sure you want to clear Alarm?', cancel: 'No', ok: 'Yes'); + _clearAlarm(AlarmInfo alarm,BuildContext context) async { + var res = await confirm(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; @@ -283,8 +284,8 @@ class _AlarmCardState extends TbContextState { } } - _ackAlarm(AlarmInfo alarm) async { - var res = await confirm(title: 'Acknowledge Alarm', message: 'Are you sure you want to acknowledge Alarm?', cancel: 'No', ok: 'Yes'); + _ackAlarm(AlarmInfo alarm,BuildContext context) async { + var res = await confirm(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 718875d..5d14718 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,16 @@ 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 e821f20..1ee54e9 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'; @@ -50,7 +51,7 @@ class _AuditLogDetailsPageState extends TbContextState { fontSize: 16, height: 20 / 16 )), - Text('Audit log details', style: TextStyle( + Text('${S.of(context).auditLogDetails}', style: TextStyle( color: Theme.of(context).primaryTextTheme.headline6!.color!.withAlpha((0.38 * 255).ceil()), fontSize: 12, fontWeight: FontWeight.normal, @@ -65,22 +66,22 @@ 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', encoder.convert(widget.auditLog.actionData)) + 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', widget.auditLog.actionFailureDetails!) + 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 2f846aa..ec4d192 100644 --- a/lib/modules/device/device_profiles_base.dart +++ b/lib/modules/device/device_profiles_base.dart @@ -8,6 +8,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, @@ -409,7 +410,7 @@ Widget _buildDeviceCount(BuildContext context, bool active, int count) { ], ), SizedBox(width: 8.67), - Text(active ? 'Active' : 'Inactive', style: TextStyle( + Text(active ? '${S.of(context).active}' : '${S.of(context).inactive}', style: TextStyle( fontSize: 12, fontWeight: FontWeight.w500, height: 16 / 12, diff --git a/lib/modules/device/devices_base.dart b/lib/modules/device/devices_base.dart index 07d192f..55e77e9 100644 --- a/lib/modules/device/devices_base.dart +++ b/lib/modules/device/devices_base.dart @@ -8,6 +8,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'; @@ -241,7 +242,7 @@ class _DeviceCardState extends TbContextState { fontWeight: FontWeight.normal, height: 16 / 12 )), - Text(widget.device.attribute('active') == 'true' ? 'Active' : 'Inactive', + Text(widget.device.attribute('active') == 'true' ? '${S.of(context).active}' : '${S.of(context).inactive}', style: TextStyle( color: widget.device.attribute('active') == 'true' ? Color(0xFF008A00) : Color(0xFFAFAFAF), fontSize: 12, diff --git a/lib/modules/device/devices_list_page.dart b/lib/modules/device/devices_list_page.dart index a7005ae..3fba82b 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'; @@ -38,10 +39,10 @@ class _DevicesListPageState extends TbPageState { onSearch: (searchText) => _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, diff --git a/lib/modules/main/main_page.dart b/lib/modules/main/main_page.dart index 3991d44..309d05f 100644 --- a/lib/modules/main/main_page.dart +++ b/lib/modules/main/main_page.dart @@ -3,6 +3,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/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'; @@ -11,7 +12,7 @@ import 'package:thingsboard_client/thingsboard_client.dart'; class TbMainNavigationItem { final Widget page; - final String title; + String title; final Icon icon; final String path; @@ -24,8 +25,10 @@ class TbMainNavigationItem { static Map> mainPageStateMap = { Authority.SYS_ADMIN: Set.unmodifiable(['/home', '/more']), - Authority.TENANT_ADMIN: Set.unmodifiable(['/home', '/alarms', '/devices', '/more']), - Authority.CUSTOMER_USER: Set.unmodifiable(['/home', '/alarms', '/devices', '/more']), + Authority.TENANT_ADMIN: Set.unmodifiable( + ['/home', '/alarms', '/devices', '/more']), + Authority.CUSTOMER_USER: Set.unmodifiable( + ['/home', '/alarms', '/devices', '/more']), }; static bool isMainPageState(TbContext tbContext, String path) { @@ -47,26 +50,26 @@ class TbMainNavigationItem { path: '/home' ) ]; - switch(tbContext.tbClient.getAuthUser()!.authority) { + switch (tbContext.tbClient.getAuthUser()!.authority) { case Authority.SYS_ADMIN: break; case Authority.TENANT_ADMIN: case Authority.CUSTOMER_USER: - items.addAll([ - TbMainNavigationItem( - page: AlarmsPage(tbContext), - title: 'Alarms', - icon: Icon(Icons.notifications), - path: '/alarms' - ), - TbMainNavigationItem( - page: DevicesMainPage(tbContext), - title: 'Devices', - icon: Icon(Icons.devices_other), - path: '/devices' - ) - ]); - break; + items.addAll([ + TbMainNavigationItem( + page: AlarmsPage(tbContext), + title: 'Alarms', + icon: Icon(Icons.notifications), + path: '/alarms' + ), + TbMainNavigationItem( + page: DevicesMainPage(tbContext), + title: 'Devices', + icon: Icon(Icons.devices_other), + path: '/devices' + ) + ]); + break; case Authority.REFRESH_TOKEN: break; case Authority.ANONYMOUS: @@ -83,8 +86,32 @@ 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 { final String _path; @@ -132,6 +159,7 @@ class _MainPageState extends TbPageState with TbMainState, TickerProvi @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 324ea6d..be43937 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 { @@ -45,7 +46,7 @@ class _MorePageState extends TbContextState { ) ), SizedBox(height: 2), - Text(_getAuthorityName(), + Text(_getAuthorityName(context), style: TextStyle( color: Color(0xFFAFAFAF), fontWeight: FontWeight.normal, @@ -71,7 +72,7 @@ class _MorePageState extends TbContextState { 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, @@ -95,7 +96,7 @@ 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( @@ -151,20 +152,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; } } @@ -184,7 +185,7 @@ class MoreMenuItem { 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) { @@ -193,17 +194,17 @@ 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', + title: '${S.of(context).asserts}', icon: Icons.domain, path: '/assets' ), MoreMenuItem( - title: 'Audit Logs', + title: '${S.of(context).auditLogs}', icon: Icons.track_changes, path: '/auditLogs' ) @@ -212,7 +213,7 @@ class MoreMenuItem { case Authority.CUSTOMER_USER: items.addAll([ MoreMenuItem( - title: 'Assets', + title: '${S.of(context).asserts}', icon: Icons.domain, path: '/assets' ) diff --git a/lib/modules/profile/change_password_page.dart b/lib/modules/profile/change_password_page.dart index 410ad19..007c33b 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'; @@ -31,7 +32,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: [ @@ -50,11 +51,11 @@ class _ChangePasswordPageState extends TbContextState { valueListenable: _showCurrentPasswordNotifier, builder: (BuildContext context, bool showPassword, child) { return FormBuilderTextField( - name: 'currentPassword', + name: '${S.of(context).currentPassword}', obscureText: !showPassword, autofocus: true, validator: FormBuilderValidators.compose([ - FormBuilderValidators.required(context, errorText: 'Current password is required.') + FormBuilderValidators.required(context, errorText: '${S.of(context).currentPasswordRequireText}') ]), decoration: InputDecoration( suffixIcon: IconButton( @@ -64,7 +65,7 @@ class _ChangePasswordPageState extends TbContextState { }, ), border: OutlineInputBorder(), - labelText: 'Current password *' + labelText: '${S.of(context).currentPasswordStar}' ), ); } @@ -74,10 +75,10 @@ class _ChangePasswordPageState extends TbContextState { valueListenable: _showNewPasswordNotifier, builder: (BuildContext context, bool showPassword, child) { return FormBuilderTextField( - name: 'newPassword', + name: '${S.of(context).newPassword}', obscureText: !showPassword, validator: FormBuilderValidators.compose([ - FormBuilderValidators.required(context, errorText: 'New password is required.') + FormBuilderValidators.required(context, errorText: '${S.of(context).newPasswordRequireText}') ]), decoration: InputDecoration( suffixIcon: IconButton( @@ -87,7 +88,7 @@ class _ChangePasswordPageState extends TbContextState { }, ), border: OutlineInputBorder(), - labelText: 'New password *' + labelText: '${S.of(context).newPasswordStar}' ), ); } @@ -97,10 +98,10 @@ class _ChangePasswordPageState extends TbContextState { valueListenable: _showNewPassword2Notifier, builder: (BuildContext context, bool showPassword, child) { return FormBuilderTextField( - name: 'newPassword2', + name: '${S.of(context).newPassword2}', obscureText: !showPassword, validator: FormBuilderValidators.compose([ - FormBuilderValidators.required(context, errorText: 'New password again is required.') + FormBuilderValidators.required(context, errorText: '${S.of(context).newPassword2RequireText}') ]), decoration: InputDecoration( suffixIcon: IconButton( @@ -110,7 +111,7 @@ class _ChangePasswordPageState extends TbContextState { }, ), border: OutlineInputBorder(), - labelText: 'New password again *' + labelText: '${S.of(context).newPassword2Star}' ), ); } @@ -122,7 +123,7 @@ class _ChangePasswordPageState extends TbContextState { onPressed: () { _changePassword(); }, - child: Center(child: Text('Change Password')) + child: Center(child: Text('${S.of(context).changePassword}')) ) ] ), @@ -158,7 +159,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 bd570b8..5c7000d 100644 --- a/lib/modules/profile/profile_page.dart +++ b/lib/modules/profile/profile_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/widgets.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'; @@ -80,30 +81,30 @@ class _ProfilePageState extends TbPageState { children: [ SizedBox(height: 16), FormBuilderTextField( - name: 'email', + name: '${S.of(context).email}', validator: FormBuilderValidators.compose([ - FormBuilderValidators.required(context, errorText: 'Email is required.'), - FormBuilderValidators.email(context, errorText: 'Invalid email format.') + FormBuilderValidators.required(context, errorText: '${S.of(context).emailRequireText}'), + FormBuilderValidators.email(context, errorText: '${S.of(context).emailInvalidText}') ]), decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'Email *' + labelText: '${S.of(context).emailStar}' ), ), SizedBox(height: 24), FormBuilderTextField( - name: 'firstName', + name: '${S.of(context).firstName}', decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'First Name' + labelText: '${S.of(context).firstNameUpper}' ), ), SizedBox(height: 24), FormBuilderTextField( - name: 'lastName', + name: '${S.of(context).lastName}', decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'Last Name' + labelText: '${S.of(context).lastNameUpper}' ), ), SizedBox(height: 24), @@ -113,7 +114,7 @@ class _ProfilePageState extends TbPageState { onPressed: () { _changePassword(); }, - child: Center(child: Text('Change Password')) + child: Center(child: Text('${S.of(context).changePassword}')) ) ] ), @@ -170,7 +171,7 @@ class _ProfilePageState extends TbPageState { _setUser(); await Future.delayed(Duration(milliseconds: 300)); _isLoadingNotifier.value = false; - showSuccessNotification('Profile successfully updated', duration: Duration(milliseconds: 1500)); + showSuccessNotification('${S.of(context).profileSuccessNotification}', duration: Duration(milliseconds: 1500)); } } } @@ -178,7 +179,7 @@ class _ProfilePageState extends TbPageState { _changePassword() async { var res = await tbContext.showFullScreenDialog(new ChangePasswordPage(tbContext)); if (res == true) { - showSuccessNotification('Password successfully changed', duration: Duration(milliseconds: 1500)); + showSuccessNotification('${S.of(context).passwordSuccessNotification}', duration: Duration(milliseconds: 1500)); } } } diff --git a/pubspec.yaml b/pubspec.yaml index 465117a..af0acce 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -41,6 +41,8 @@ dependencies: form_builder_validators: ^7.2.0 universal_platform: ^1.0.0+1 preload_page_view: ^0.1.6 + flutter_localizations: + sdk: flutter dev_dependencies: flutter_test: @@ -83,3 +85,5 @@ flutter_icons: ios: true remove_alpha_ios: true image_path: "assets/images/thingsboard.png" +flutter_intl: + enabled: true