Skip to content

Commit 8b94b29

Browse files
committed
login: translations
1 parent 2a8b6a3 commit 8b94b29

File tree

2 files changed

+124
-21
lines changed

2 files changed

+124
-21
lines changed

assets/l10n/app_en.arb

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,82 @@
8383
"@lightboxCopyLinkSuccessToast": {
8484
"description": "Success message in lightbox after copy link action completed."
8585
},
86+
"loginPageTitle": "Log in",
87+
"@loginPageTitle": {
88+
"description": "Page title for login page."
89+
},
90+
"loginFormSubmitLabel": "Log in",
91+
"@loginFormSubmitLabel": {
92+
"description": "Button text to submit login credentials."
93+
},
94+
"loginAddAnAccount": "Add an account",
95+
"@loginAddAnAccount": {
96+
"description": "Page title for screen to add a zulip account."
97+
},
98+
"loginServerLoginFailed": "Login failed",
99+
"@loginServerLoginFailed": {
100+
"description": "Error dialog title when login for a Zulip server fails."
101+
},
102+
"loginServerUrlInputLabel": "Your Zulip server URL",
103+
"@loginServerUrlInputLabel": {
104+
"description": "Input label in login page for Zulip server URL entry."
105+
},
106+
"loginServerErrorMessage": "The server said:\n\n{message}",
107+
"@loginServerErrorMessage": {
108+
"description": "Error message for login that quotes an error from the server.",
109+
"placeholders": {
110+
"message": {
111+
"type": "String",
112+
"example": "Invalid format"
113+
}
114+
}
115+
},
116+
"loginCouldNotConnectMessage": "Failed to connect to server:\n{url}",
117+
"@loginCouldNotConnectMessage": {
118+
"description": "Message for login dialog the app could not connect to the server.",
119+
"placeholders": {
120+
"url": {
121+
"type": "String",
122+
"example": "http://example.com/"
123+
}
124+
}
125+
},
126+
"loginCouldNotConnectTitle": "Could not connect",
127+
"@loginCouldNotConnectTitle": {
128+
"description": "Title for login dialog the app could not connect to the server."
129+
},
130+
"loginHidePassword": "Hide password",
131+
"@loginHidePassword": {
132+
"description": "Icon label for button to hide password in input form."
133+
},
134+
"loginInvalidInput": "Invalid input",
135+
"@loginInvalidInput": {
136+
"description": "Title for login dialog when input is invalid."
137+
},
138+
"loginValidationPassword": "Please enter your password",
139+
"@loginValidationPassword": {
140+
"description": "Prompt for input for password field."
141+
},
142+
"loginValidationPasswordLabel": "Password",
143+
"@loginValidationPasswordLabel": {
144+
"description": "Label for input for password field."
145+
},
146+
"loginValidationRequireEmail": "Please enter your email",
147+
"@loginValidationRequireEmail": {
148+
"description": "Prompt when an email is required to login."
149+
},
150+
"loginValidationRequireEmailLabel": "Email",
151+
"@loginValidationRequireEmailLabel": {
152+
"description": "Label for input when an email is required to login."
153+
},
154+
"loginValidationRequireUsername": "Please enter your username",
155+
"@loginValidationRequireUsername": {
156+
"description": "Prompt when a username is required to login."
157+
},
158+
"loginValidationRequireUsernameLabel": "Username",
159+
"@loginValidationRequireUsernameLabel": {
160+
"description": "Label for input when a username is required to login."
161+
},
86162
"subscribedToNStreams": "Subscribed to {num, plural, =0{no streams} =1{1 stream} other{{num} streams}}",
87163
"@subscribedToNStreams": {
88164
"description": "Test page label showing number of streams user is subscribed to.",
@@ -113,6 +189,22 @@
113189
}
114190
}
115191
},
192+
"serverUrlValidationErrorEmpty": "Please enter a URL.",
193+
"@serverUrlValidationErrorEmpty": {
194+
"description": "Server validation error message when URL is empty"
195+
},
196+
"serverUrlValidationErrorInvalidUrl": "Please enter a valid URL.",
197+
"@serverUrlValidationErrorInvalidUrl": {
198+
"description": "Server validation error message when URL is not in a valid format."
199+
},
200+
"serverUrlValidationErrorNoUseEmail": "Please enter the server URL, not your email.",
201+
"@serverUrlValidationErrorNoUseEmail": {
202+
"description": "Server validation error message when URL provided looks like an email"
203+
},
204+
"serverUrlValidationErrorUnsupportedScheme": "The server URL must start with http:// or https://.",
205+
"@serverUrlValidationErrorUnsupportedScheme": {
206+
"description": "Server validation error message when URL does not have a scheme defined."
207+
},
116208
"userRoleOwner": "Owner",
117209
"@userRoleOwner": {
118210
"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 translatedMessage(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.loginInvalidInput,
145+
message: error.translatedMessage(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.loginCouldNotConnectTitle,
165+
message: zulipLocalizations.loginCouldNotConnectMessage(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.translatedMessage(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.dialogButtonContinue)),
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.loginServerErrorMessage(e.toTranslatedString(zulipLocalizations))
294300
: e.message;
295-
showErrorDialog(context: context, title: 'Login failed', message: message);
301+
showErrorDialog(context: context,
302+
title: zulipLocalizations.loginServerLoginFailed,
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)