Skip to content

Commit 0e2bf00

Browse files
committed
intl: Change some hard-coded strings to be localized
Showcasing using full static strings in widgets, strings with placeholders that need to respond to pluralization, and strings in error dialogs.
1 parent c4e8177 commit 0e2bf00

File tree

6 files changed

+100
-21
lines changed

6 files changed

+100
-21
lines changed

assets/l10n/app_en.arb

+60-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,60 @@
1-
{}
1+
{
2+
"chooseAccountPageTitle": "Choose account",
3+
"@chooseAccountPageTitle": {
4+
"description": "Title for ChooseAccountPage"
5+
},
6+
"chooseAccountButtonAddAnAccount": "Add an account",
7+
"@chooseAccountButtonAddAnAccount": {
8+
"description": "Label for ChooseAccountPage button to add an account"
9+
},
10+
"profileButtonSendDirectMessage": "Send direct message",
11+
"@profileButtonSendDirectMessage": {
12+
"description": "Label for button in profile screen to navigate to DMs with the shown user."
13+
},
14+
"cameraAccessDeniedTitle": "Permissions needed",
15+
"@cameraAccessDeniedTitle": {
16+
"description": "Title for dialog when the user needs to grant permissions for camera access."
17+
},
18+
"cameraAccessDeniedMessage": "To upload an image, please grant Zulip additional permissions in Settings.",
19+
"@cameraAccessDeniedMessage": {
20+
"description": "Message for dialog when the user needs to grant permissions for camera access."
21+
},
22+
"cameraAccessDeniedButtonText": "Open settings",
23+
"@cameraAccessDeniedButtonText": {
24+
"description": "Message for dialog when the user needs to grant permissions for camera access."
25+
},
26+
"subscribedToNStreams": "Subscribed to {num, plural, =0{no streams} =1{1 stream} other{{num} streams}}",
27+
"@subscribedToNStreams": {
28+
"description": "Test page label showing number of streams user is subscribed to.",
29+
"placeholders": {
30+
"num": {
31+
"type": "int",
32+
"example": "4"
33+
}
34+
}
35+
},
36+
"userRoleOwner": "Owner",
37+
"@userRoleOwner": {
38+
"description": "Label for UserRole.owner"
39+
},
40+
"userRoleAdministrator": "Administrator",
41+
"@userRoleAdministrator": {
42+
"description": "Label for UserRole.administrator"
43+
},
44+
"userRoleModerator": "Moderator",
45+
"@userRoleModerator": {
46+
"description": "Label for UserRole.moderator"
47+
},
48+
"userRoleMember": "Member",
49+
"@userRoleMember": {
50+
"description": "Label for UserRole.member"
51+
},
52+
"userRoleGuest": "Guest",
53+
"@userRoleGuest": {
54+
"description": "Label for UserRole.guest"
55+
},
56+
"userRoleUnknown": "Unknown",
57+
"@userRoleUnknown": {
58+
"description": "Label for UserRole.unknown"
59+
}
60+
}

assets/l10n/app_ja.arb

+15-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,15 @@
1-
{}
1+
{
2+
"chooseAccountPageTitle": "アカウントを選択",
3+
"chooseAccountButtonAddAnAccount": "新しいアカウントを追加",
4+
"profileButtonSendDirectMessage": "ダイレクトメッセージを送信",
5+
"cameraAccessDeniedTitle": "権限が必要です",
6+
"cameraAccessDeniedMessage": "画像をアップロードするには、「設定」で Zulip に追加の権限を許可してください。",
7+
"cameraAccessDeniedButtonText": "設定を開く",
8+
"subscribedToNStreams": "{num, plural, other{{num}つのストリームをフォローしています}}",
9+
"userRoleOwner": "オーナー",
10+
"userRoleAdministrator": "管理者",
11+
"userRoleModerator": "モデレータ",
12+
"userRoleMember": "メンバー",
13+
"userRoleGuest": "ゲスト",
14+
"userRoleUnknown": "不明"
15+
}

lib/widgets/app.dart

+5-6
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,12 @@ class ChooseAccountPage extends StatelessWidget {
7979

8080
@override
8181
Widget build(BuildContext context) {
82+
final zulipLocalizations = ZulipLocalizations.of(context);
8283
assert(!PerAccountStoreWidget.debugExistsOf(context));
8384
final globalStore = GlobalStoreWidget.of(context);
8485
return Scaffold(
8586
appBar: AppBar(
86-
title: const Text('Choose account'),
87+
title: Text(zulipLocalizations.chooseAccountPageTitle),
8788
actions: const [ChooseAccountPageOverflowButton()]),
8889
body: SafeArea(
8990
minimum: const EdgeInsets.all(8),
@@ -100,7 +101,7 @@ class ChooseAccountPage extends StatelessWidget {
100101
ElevatedButton(
101102
onPressed: () => Navigator.push(context,
102103
AddAccountPage.buildRoute()),
103-
child: const Text('Add an account')),
104+
child: Text(zulipLocalizations.chooseAccountButtonAddAnAccount)),
104105
]))),
105106
));
106107
}
@@ -140,6 +141,7 @@ class HomePage extends StatelessWidget {
140141
@override
141142
Widget build(BuildContext context) {
142143
final store = PerAccountStoreWidget.of(context);
144+
final zulipLocalizations = ZulipLocalizations.of(context);
143145

144146
InlineSpan bold(String text) => TextSpan(
145147
text: text, style: const TextStyle(fontWeight: FontWeight.bold));
@@ -164,10 +166,7 @@ class HomePage extends StatelessWidget {
164166
Text.rich(TextSpan(
165167
text: 'Zulip server version: ',
166168
children: [bold(store.zulipVersion)])),
167-
Text.rich(TextSpan(text: 'Subscribed to ', children: [
168-
bold(store.subscriptions.length.toString()),
169-
const TextSpan(text: ' streams'),
170-
])),
169+
Text(zulipLocalizations.subscribedToNStreams(store.subscriptions.length)),
171170
])),
172171
const SizedBox(height: 16),
173172
ElevatedButton(

lib/widgets/compose_box.dart

+6-4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import 'package:app_settings/app_settings.dart';
22
import 'package:file_picker/file_picker.dart';
33
import 'package:flutter/material.dart';
44
import 'package:flutter/services.dart';
5+
import 'package:flutter_gen/gen_l10n/zulip_localizations.dart';
56
import 'package:image_picker/image_picker.dart';
67

78
import '../api/model/model.dart';
@@ -603,6 +604,7 @@ class _AttachFromCameraButton extends _AttachUploadsButton {
603604

604605
@override
605606
Future<Iterable<_File>> getFiles(BuildContext context) async {
607+
final zulipLocalizations = ZulipLocalizations.of(context);
606608
final picker = ImagePicker();
607609
final XFile? result;
608610
try {
@@ -619,10 +621,10 @@ class _AttachFromCameraButton extends _AttachUploadsButton {
619621
// permission-request alert once, the first time the app wants to
620622
// use a protected resource. After that, the only way the user can
621623
// grant it is in Settings.
622-
showSuggestedActionDialog(context: context, // TODO(i18n)
623-
title: 'Permissions needed',
624-
message: 'To upload an image, please grant Zulip additional permissions in Settings.',
625-
actionButtonText: 'Open settings',
624+
showSuggestedActionDialog(context: context,
625+
title: zulipLocalizations.cameraAccessDeniedTitle,
626+
message: zulipLocalizations.cameraAccessDeniedMessage,
627+
actionButtonText: zulipLocalizations.cameraAccessDeniedButtonText,
626628
onActionButtonPress: () {
627629
AppSettings.openAppSettings();
628630
});

lib/widgets/profile.dart

+11-9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'dart:convert';
22

33
import 'package:flutter/material.dart';
4+
import 'package:flutter_gen/gen_l10n/zulip_localizations.dart';
45

56
import '../api/model/model.dart';
67
import '../model/content.dart';
@@ -28,6 +29,7 @@ class ProfilePage extends StatelessWidget {
2829

2930
@override
3031
Widget build(BuildContext context) {
32+
final zulipLocalizations = ZulipLocalizations.of(context);
3133
final store = PerAccountStoreWidget.of(context);
3234
final user = store.users[userId];
3335
if (user == null) {
@@ -42,7 +44,7 @@ class ProfilePage extends StatelessWidget {
4244
textAlign: TextAlign.center,
4345
style: _TextStyles.primaryFieldText.merge(const TextStyle(fontWeight: FontWeight.bold))),
4446
// TODO(#291) render email field
45-
Text(roleToLabel(user.role),
47+
Text(roleToLabel(user.role, zulipLocalizations),
4648
textAlign: TextAlign.center,
4749
style: _TextStyles.primaryFieldText),
4850
// TODO(#197) render user status
@@ -56,7 +58,7 @@ class ProfilePage extends StatelessWidget {
5658
MessageListPage.buildRoute(context: context,
5759
narrow: DmNarrow.withUser(userId, selfUserId: store.account.userId))),
5860
icon: const Icon(Icons.email),
59-
label: const Text('Send direct message')),
61+
label: Text(zulipLocalizations.profileButtonSendDirectMessage)),
6062
];
6163

6264
return Scaffold(
@@ -93,14 +95,14 @@ class _ProfileErrorPage extends StatelessWidget {
9395
}
9496
}
9597

96-
String roleToLabel(UserRole role) {
98+
String roleToLabel(UserRole role, ZulipLocalizations zulipLocalizations) {
9799
return switch (role) {
98-
UserRole.owner => 'Owner',
99-
UserRole.administrator => 'Administrator',
100-
UserRole.moderator => 'Moderator',
101-
UserRole.member => 'Member',
102-
UserRole.guest => 'Guest',
103-
UserRole.unknown => 'Unknown',
100+
UserRole.owner => zulipLocalizations.userRoleOwner,
101+
UserRole.administrator => zulipLocalizations.userRoleAdministrator,
102+
UserRole.moderator => zulipLocalizations.userRoleModerator,
103+
UserRole.member => zulipLocalizations.userRoleMember,
104+
UserRole.guest => zulipLocalizations.userRoleGuest,
105+
UserRole.unknown => zulipLocalizations.userRoleUnknown,
104106
};
105107
}
106108

test/widgets/profile_test.dart

+3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'package:checks/checks.dart';
22
import 'package:flutter/foundation.dart';
33
import 'package:flutter/material.dart';
4+
import 'package:flutter_gen/gen_l10n/zulip_localizations.dart';
45
import 'package:flutter_test/flutter_test.dart';
56
import 'package:url_launcher/url_launcher.dart';
67
import 'package:zulip/api/model/initial_snapshot.dart';
@@ -44,6 +45,8 @@ Future<void> setupPage(WidgetTester tester, {
4445
GlobalStoreWidget(
4546
child: MaterialApp(
4647
navigatorObservers: navigatorObserver != null ? [navigatorObserver] : [],
48+
localizationsDelegates: ZulipLocalizations.localizationsDelegates,
49+
supportedLocales: ZulipLocalizations.supportedLocales,
4750
home: PerAccountStoreWidget(
4851
accountId: eg.selfAccount.id,
4952
child: ProfilePage(userId: pageUserId)))));

0 commit comments

Comments
 (0)