1
1
# Translations
2
2
3
3
Our goal is for this app to be localized and offered in many
4
- languages, just like zulip-mobile and zulip web.
4
+ languages, just like zulip-mobile and Zulip web.
5
+
5
6
6
7
## Current state
7
8
8
- Currently in place is integration with ` flutter_localizations `
9
- package, allowing all flutter UI elements to be localized
9
+ We have a framework set up that makes it possible for UI strings
10
+ to be translated. (This was issue #275 .) This means that when
11
+ adding new strings to the UI, instead of using a constant string
12
+ in English we'll add the string to that framework.
13
+ For details, see below.
14
+
15
+ At present not all of the codebase has been migrated to use the framework,
16
+ so you'll see some existing code that uses constant strings.
17
+ Fixing that is issue #277 .
18
+
19
+ At present we don't have the strings wired up to a platform for
20
+ people to contribute translations. That's issue #276 .
21
+ Until then, we have only a handful of strings actually translated,
22
+ just to make it possible to demonstrate the framework
23
+ is working correctly.
24
+
10
25
11
- Per the discussion in #275 the approach here is to start with
12
- ARB files and have dart autogenerate the bindings. I believe
13
- this is the most straightforward way when connecting with a
14
- translation management system, as they output ARB files that
15
- we consume (this is also the same way web and mobile works
16
- but with .po or .json files, I believe).
26
+ ## Adding new UI strings
17
27
18
- ## Adding new strings
28
+ ### Adding a string to the translation database
19
29
20
- Add the appropriate entry in ` assets/l10n/app_en.arb ` ensuring
21
- you add a corresponding resource attribute describing the
22
- string in context. Example:
30
+ To add a new string in the UI, start by
31
+ adding an entry in the ARB file ` assets/l10n/app_en.arb ` .
32
+ This includes a name that you choose for the string,
33
+ its value in English,
34
+ and a "resource attribute" describing the string in context.
35
+ The name will become an identifier in our Dart code.
36
+ The description will provide context for people contributing translations.
23
37
38
+ For example, this entry describes a UI string
39
+ named ` profileButtonSendDirectMessage `
40
+ which appears in English as "Send direct message":
24
41
```
25
42
"profileButtonSendDirectMessage": "Send direct message",
26
43
"@profileButtonSendDirectMessage": {
27
44
"description": "Label for button in profile screen to navigate to DMs with the shown user."
28
45
},
29
46
```
30
47
31
- The bindings are automatically generated when you execute
32
- ` flutter run ` although you can also manually trigger it
33
- using ` flutter gen-l10n ` .
48
+ Then run the app (with ` flutter run ` or in your IDE),
49
+ or perform a hot reload,
50
+ to cause the Dart bindings to be updated based on your
51
+ changes to the ARB file.
52
+ (You can also trigger an update directly, with ` flutter gen-l10n ` .)
34
53
35
- Untranslated strings will be included in a generated
36
- ` build/untranslated_messages.json ` file. This output
37
- awaits #276 .
38
54
39
- ## Using in code
55
+ ### Using a translated string in the code
40
56
41
- To utilize in our widgets you need to import the generated
42
- bindings:
57
+ To use in our widgets, you need to import the generated bindings:
43
58
```
44
59
import 'package:flutter_gen/gen_l10n/zulip_localizations.dart';
45
60
```
46
61
47
- And in your widget code pull the localizations out of the context:
62
+ Then in your widget code, pull the localizations object
63
+ off of the Flutter build context:
48
64
```
49
65
Widget build(BuildContext context) {
50
66
final zulipLocalizations = ZulipLocalizations.of(context);
51
67
```
52
68
53
- And finally access one of the generated properties:
54
- ` Text(zulipLocalizations.chooseAccountButtonAddAnAccount) ` .
69
+ Finally, on the localizations object use the getter
70
+ that was generated for the new string:
71
+ ` Text(zulipLocalizations.profileButtonSendDirectMessage) ` .
72
+
73
+
74
+ ### Strings with placeholders
75
+
76
+ When a UI string is a constant per language, with no placeholders,
77
+ the generated Dart code provides a simple getter, as seen above.
78
+
79
+ When the string takes a placeholder,
80
+ the generated Dart binding for it will instead be a function,
81
+ taking arguments corresponding to the placeholders.
55
82
56
- String that take placeholders are generated as functions
57
- that take arguments: ` zulipLocalizations.subscribedToNStreams(store.subscriptions.length) `
83
+ For example:
84
+ ` zulipLocalizations.subscribedToNStreams(store.subscriptions.length) ` .
58
85
59
- ## Hack to enforce locale (for testing, etc)
60
86
61
- To manually trigger a locale change for testing I've found
62
- it helpful to add the ` localeResolutionCallback ` in
63
- ` app.dart ` to enforce a particular locale:
87
+ ## Hack to enforce locale (for testing, etc.)
88
+
89
+ For testing the app's behavior in different locales,
90
+ you can use your device's system settings to
91
+ change the preferred language.
92
+
93
+ Alternatively, you may find it helpful to
94
+ pass a ` localeResolutionCallback ` to the ` MaterialApp ` in ` app.dart `
95
+ to enforce a particular locale:
64
96
65
97
```
66
98
return GlobalStoreWidget(
@@ -75,16 +107,20 @@ return GlobalStoreWidget(
75
107
home: const ChooseAccountPage()));
76
108
```
77
109
78
- (careful that returning a locale not in ` supportedLocales `
79
- will crash, the default behavior ensures a fallback is
80
- always selected)
110
+ (When using this hack, returning a locale not in ` supportedLocales ` will
111
+ cause a crash.
112
+ The default behavior without ` localeResolutionCallback ` ensures
113
+ a fallback is always selected.)
114
+
81
115
82
116
## Tests
83
117
84
- Widgets that access localization will fail if the root
85
- ` MaterialApp ` given in the setup isn't also set up with
86
- localizations. Make sure to add the right
87
- ` localizationDelegates ` and ` supportedLocales ` :
118
+ Widgets that access localizations will fail if
119
+ the ambient ` MaterialApp ` isn't set up for localizations.
120
+ For the ` MaterialApp ` used in the app, we do this in ` app.dart ` .
121
+ In tests, this typically requires a test's setup code to provide
122
+ arguments ` localizationDelegates ` and ` supportedLocales ` .
123
+ For example:
88
124
89
125
```
90
126
await tester.pumpWidget(
@@ -95,3 +131,21 @@ localizations. Make sure to add the right
95
131
supportedLocales: ZulipLocalizations.supportedLocales,
96
132
home: PerAccountStoreWidget(
97
133
```
134
+
135
+
136
+ ## Other notes
137
+
138
+ Our approach uses the ` flutter_localizations ` package.
139
+ We use the ` gen_l10n ` way, where we write ARB files
140
+ and the tool generates the Dart bindings.
141
+
142
+ As discussed in issue #275 , the other way around was
143
+ also an option. But this way seems most straightforward
144
+ when connecting with a translation management system,
145
+ as they output ARB files that we consume.
146
+ This also parallels how zulip-mobile works with ` .json ` files
147
+ (and Zulip web, and the Zulip server with ` .po ` files?)
148
+
149
+ A file ` build/untranslated_messages.json ` is emitted
150
+ whenever the Dart bindings are generated from the ARB files.
151
+ This output awaits #276 .
0 commit comments