Skip to content

Commit f180881

Browse files
committed
login: Translations
1 parent 867c830 commit f180881

File tree

2 files changed

+128
-21
lines changed

2 files changed

+128
-21
lines changed

assets/l10n/app_en.arb

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,42 @@
6363
"@errorCopyingFailed": {
6464
"description": "Dialog message when copying the text of a message to the users system clipboard failed."
6565
},
66+
"errorLoginInvalidInputTitle": "Invalid input",
67+
"@errorLoginInvalidInputTitle": {
68+
"description": "Error dialog title for login dialog when input is invalid."
69+
},
70+
"errorLoginFailed": "Login failed",
71+
"@errorLoginFailed": {
72+
"description": "Error dialog title when login for a Zulip server fails."
73+
},
6674
"errorMessageDoesNotSeemToExist": "That message does not seem to exist.",
6775
"@errorMessageDoesNotSeemToExist": {
6876
"description": "Error message when loading a message that does not exist."
6977
},
78+
"errorServerErrorMessage": "The server said:\n\n{message}",
79+
"@errorServerErrorMessage": {
80+
"description": "Error message that quotes an error from the server.",
81+
"placeholders": {
82+
"message": {
83+
"type": "String",
84+
"example": "Invalid format"
85+
}
86+
}
87+
},
88+
"errorLoginCouldNotConnect": "Failed to connect to server:\n{url}",
89+
"@errorLoginCouldNotConnect": {
90+
"description": "Error message when the app could not connect to the server.",
91+
"placeholders": {
92+
"url": {
93+
"type": "String",
94+
"example": "http://example.com/"
95+
}
96+
}
97+
},
98+
"errorLoginCouldNotConnectTitle": "Could not connect",
99+
"@errorLoginCouldNotConnectTitle": {
100+
"description": "Error title when the app could not connect to the server."
101+
},
70102
"errorQuotationFailed": "Quotation failed",
71103
"@errorQuotationFailed": {
72104
"description": "Error dialog message when quoting a message failed."
@@ -95,6 +127,54 @@
95127
"@lightboxCopyLinkTooltip": {
96128
"description": "Tooltip in lightbox for the copy link action."
97129
},
130+
"loginPageTitle": "Log in",
131+
"@loginPageTitle": {
132+
"description": "Page title for login page."
133+
},
134+
"loginFormSubmitLabel": "Log in",
135+
"@loginFormSubmitLabel": {
136+
"description": "Button text to submit login credentials."
137+
},
138+
"loginAddAnAccount": "Add an account",
139+
"@loginAddAnAccount": {
140+
"description": "Page title for screen to add a zulip account."
141+
},
142+
"loginAddAnAccountConfirm": "Continue",
143+
"@loginAddAnAccountConfirm": {
144+
"description": "Button label to submit form for adding an account."
145+
},
146+
"loginServerUrlInputLabel": "Your Zulip server URL",
147+
"@loginServerUrlInputLabel": {
148+
"description": "Input label in login page for Zulip server URL entry."
149+
},
150+
"loginHidePassword": "Hide password",
151+
"@loginHidePassword": {
152+
"description": "Icon label for button to hide password in input form."
153+
},
154+
"loginValidationPassword": "Please enter your password",
155+
"@loginValidationPassword": {
156+
"description": "Prompt for input for password field."
157+
},
158+
"loginValidationPasswordLabel": "Password",
159+
"@loginValidationPasswordLabel": {
160+
"description": "Label for input for password field."
161+
},
162+
"loginValidationRequireEmail": "Please enter your email",
163+
"@loginValidationRequireEmail": {
164+
"description": "Prompt when an email is required to login."
165+
},
166+
"loginValidationRequireEmailLabel": "Email",
167+
"@loginValidationRequireEmailLabel": {
168+
"description": "Label for input when an email is required to login."
169+
},
170+
"loginValidationRequireUsername": "Please enter your username",
171+
"@loginValidationRequireUsername": {
172+
"description": "Prompt when a username is required to login."
173+
},
174+
"loginValidationRequireUsernameLabel": "Username",
175+
"@loginValidationRequireUsernameLabel": {
176+
"description": "Label for input when a username is required to login."
177+
},
98178
"subscribedToNStreams": "Subscribed to {num, plural, =0{no streams} =1{1 stream} other{{num} streams}}",
99179
"@subscribedToNStreams": {
100180
"description": "Test page label showing number of streams user is subscribed to.",
@@ -125,6 +205,22 @@
125205
}
126206
}
127207
},
208+
"serverUrlValidationErrorEmpty": "Please enter a URL.",
209+
"@serverUrlValidationErrorEmpty": {
210+
"description": "Server validation error message when URL is empty"
211+
},
212+
"serverUrlValidationErrorInvalidUrl": "Please enter a valid URL.",
213+
"@serverUrlValidationErrorInvalidUrl": {
214+
"description": "Server validation error message when URL is not in a valid format."
215+
},
216+
"serverUrlValidationErrorNoUseEmail": "Please enter the server URL, not your email.",
217+
"@serverUrlValidationErrorNoUseEmail": {
218+
"description": "Server validation error message when URL provided looks like an email"
219+
},
220+
"serverUrlValidationErrorUnsupportedScheme": "The server URL must start with http:// or https://.",
221+
"@serverUrlValidationErrorUnsupportedScheme": {
222+
"description": "Server validation error message when URL does not have a scheme defined."
223+
},
128224
"userRoleOwner": "Owner",
129225
"@userRoleOwner": {
130226
"description": "Label for UserRole.owner"

lib/widgets/login.dart

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'package:flutter/material.dart';
2+
import 'package:flutter_gen/gen_l10n/zulip_localizations.dart';
23

34
import '../api/core.dart';
45
import '../api/exception.dart';
@@ -42,17 +43,17 @@ enum ServerUrlValidationError {
4243
}
4344
}
4445

45-
String message() { // TODO(i18n)
46+
String message(ZulipLocalizations zulipLocalizations) {
4647
switch (this) {
4748
case empty:
48-
return 'Please enter a URL.';
49+
return zulipLocalizations.serverUrlValidationErrorEmpty;
4950
case invalidUrl:
50-
return 'Please enter a valid URL.';
51+
return zulipLocalizations.serverUrlValidationErrorInvalidUrl;
5152
case noUseEmail:
52-
return 'Please enter the server URL, not your email.';
53+
return zulipLocalizations.serverUrlValidationErrorNoUseEmail;
5354
case unsupportedSchemeZulip:
5455
case unsupportedSchemeOther:
55-
return 'The server URL must start with http:// or https://.';
56+
return zulipLocalizations.serverUrlValidationErrorUnsupportedScheme;
5657
}
5758
}
5859
}
@@ -135,11 +136,13 @@ class _AddAccountPageState extends State<AddAccountPage> {
135136
}
136137

137138
Future<void> _onSubmitted(BuildContext context) async {
139+
final zulipLocalizations = ZulipLocalizations.of(context);
138140
final url = _parseResult.url;
139141
final error = _parseResult.error;
140142
if (error != null) {
141143
showErrorDialog(context: context,
142-
title: 'Invalid input', message: error.message());
144+
title: zulipLocalizations.errorLoginInvalidInputTitle,
145+
message: error.message(zulipLocalizations));
143146
return;
144147
}
145148
assert(url != null);
@@ -158,7 +161,8 @@ class _AddAccountPageState extends State<AddAccountPage> {
158161
// TODO(#105) give more helpful feedback; see `fetchServerSettings`
159162
// in zulip-mobile's src/message/fetchActions.js.
160163
showErrorDialog(context: context,
161-
title: 'Could not connect', message: 'Failed to connect to server:\n$url');
164+
title: zulipLocalizations.errorLoginCouldNotConnectTitle,
165+
message: zulipLocalizations.errorLoginCouldNotConnect(url.toString()));
162166
return;
163167
}
164168
// https://github.com/dart-lang/linter/issues/4007
@@ -180,13 +184,14 @@ class _AddAccountPageState extends State<AddAccountPage> {
180184
@override
181185
Widget build(BuildContext context) {
182186
assert(!PerAccountStoreWidget.debugExistsOf(context));
187+
final zulipLocalizations = ZulipLocalizations.of(context);
183188
final error = _parseResult.error;
184189
final errorText = error == null || error.shouldDeferFeedback()
185190
? null
186-
: error.message();
191+
: error.message(zulipLocalizations);
187192

188193
return Scaffold(
189-
appBar: AppBar(title: const Text('Add an account'),
194+
appBar: AppBar(title: Text(zulipLocalizations.loginAddAnAccount),
190195
bottom: _inProgress
191196
? const PreferredSize(preferredSize: Size.fromHeight(4),
192197
child: LinearProgressIndicator(minHeight: 4)) // 4 restates default
@@ -211,7 +216,7 @@ class _AddAccountPageState extends State<AddAccountPage> {
211216
// …but leave out unfocusing the input in case more editing is needed.
212217
},
213218
decoration: InputDecoration(
214-
labelText: 'Your Zulip server URL',
219+
labelText: zulipLocalizations.loginServerUrlInputLabel,
215220
errorText: errorText,
216221
helperText: kLayoutPinningHelperText,
217222
hintText: 'your-org.zulipchat.com')),
@@ -220,7 +225,7 @@ class _AddAccountPageState extends State<AddAccountPage> {
220225
onPressed: !_inProgress && errorText == null
221226
? () => _onSubmitted(context)
222227
: null,
223-
child: const Text('Continue')),
228+
child: Text(zulipLocalizations.loginAddAnAccountConfirm)),
224229
])))));
225230
}
226231
}
@@ -289,10 +294,13 @@ class _PasswordLoginPageState extends State<PasswordLoginPage> {
289294
// TODO(#105) give more helpful feedback. The RN app is
290295
// unhelpful here; we should at least recognize invalid auth errors, and
291296
// errors for deactivated user or realm (see zulip-mobile#4571).
297+
final zulipLocalizations = ZulipLocalizations.of(context);
292298
final message = (e is ZulipApiException)
293-
? 'The server said:\n\n${e.message}'
299+
? zulipLocalizations.errorServerErrorMessage(e.message)
294300
: e.message;
295-
showErrorDialog(context: context, title: 'Login failed', message: message);
301+
showErrorDialog(context: context,
302+
title: zulipLocalizations.errorLoginFailed,
303+
message: message);
296304
return;
297305
}
298306

@@ -335,6 +343,7 @@ class _PasswordLoginPageState extends State<PasswordLoginPage> {
335343
@override
336344
Widget build(BuildContext context) {
337345
assert(!PerAccountStoreWidget.debugExistsOf(context));
346+
final zulipLocalizations = ZulipLocalizations.of(context);
338347
final requireEmailFormatUsernames = widget.serverSettings.requireEmailFormatUsernames;
339348

340349
final usernameField = TextFormField(
@@ -350,8 +359,8 @@ class _PasswordLoginPageState extends State<PasswordLoginPage> {
350359
validator: (value) {
351360
if (value == null || value.trim().isEmpty) {
352361
return requireEmailFormatUsernames
353-
? 'Please enter your email.'
354-
: 'Please enter your username.';
362+
? zulipLocalizations.loginValidationRequireEmail
363+
: zulipLocalizations.loginValidationRequireUsername;
355364
}
356365
if (requireEmailFormatUsernames) {
357366
// TODO(#106): validate is in the shape of an email
@@ -360,7 +369,9 @@ class _PasswordLoginPageState extends State<PasswordLoginPage> {
360369
},
361370
textInputAction: TextInputAction.next,
362371
decoration: InputDecoration(
363-
labelText: requireEmailFormatUsernames ? 'Email address' : 'Username',
372+
labelText: requireEmailFormatUsernames
373+
? zulipLocalizations.loginValidationRequireEmailLabel
374+
: zulipLocalizations.loginValidationRequireUsernameLabel,
364375
helperText: kLayoutPinningHelperText,
365376
));
366377

@@ -372,14 +383,14 @@ class _PasswordLoginPageState extends State<PasswordLoginPage> {
372383
autovalidateMode: AutovalidateMode.onUserInteraction,
373384
validator: (value) {
374385
if (value == null || value.isEmpty) {
375-
return 'Please enter your password.';
386+
return zulipLocalizations.loginValidationPassword;
376387
}
377388
return null;
378389
},
379390
textInputAction: TextInputAction.go,
380391
onFieldSubmitted: (value) => _submit(),
381392
decoration: InputDecoration(
382-
labelText: 'Password',
393+
labelText: zulipLocalizations.loginValidationPasswordLabel,
383394
helperText: kLayoutPinningHelperText,
384395
// TODO(material-3): Simplify away `Semantics` by using IconButton's
385396
// M3-only params `isSelected` / `selectedIcon`, after fixing
@@ -389,14 +400,14 @@ class _PasswordLoginPageState extends State<PasswordLoginPage> {
389400
// [ButtonStyleButton].)
390401
suffixIcon: Semantics(toggled: _obscurePassword,
391402
child: IconButton(
392-
tooltip: 'Hide password',
403+
tooltip: zulipLocalizations.loginHidePassword,
393404
onPressed: _handlePasswordVisibilityPress,
394405
icon: _obscurePassword
395406
? const Icon(Icons.visibility_off)
396407
: const Icon(Icons.visibility)))));
397408

398409
return Scaffold(
399-
appBar: AppBar(title: const Text('Log in'),
410+
appBar: AppBar(title: Text(zulipLocalizations.loginPageTitle),
400411
bottom: _inProgress
401412
? const PreferredSize(preferredSize: Size.fromHeight(4),
402413
child: LinearProgressIndicator(minHeight: 4)) // 4 restates default
@@ -416,7 +427,7 @@ class _PasswordLoginPageState extends State<PasswordLoginPage> {
416427
const SizedBox(height: 8),
417428
ElevatedButton(
418429
onPressed: _inProgress ? null : _submit,
419-
child: const Text('Log in')),
430+
child: Text(zulipLocalizations.loginFormSubmitLabel)),
420431
])))))));
421432
}
422433
}

0 commit comments

Comments
 (0)