Skip to content

Commit 67dff51

Browse files
committed
docs: Make an editing pass on new translation docs
Mostly this revises the text to be more fully oriented toward giving instructions on what one needs to know for working on the app, and in particular for adding UI features. Also add the fun and helpful fact that a hot reload is enough to cause the bindings to get updated from the ARB files. And tweak the descriptions on a couple of strings; these label just a single item, not a section.
1 parent 3165c81 commit 67dff51

File tree

3 files changed

+97
-47
lines changed

3 files changed

+97
-47
lines changed

README.md

+3-7
Original file line numberDiff line numberDiff line change
@@ -145,14 +145,10 @@ The generated files that most frequently need an update are
145145
run `flutter pub get && flutter build ios --config-only && flutter build macos --config-only`.
146146

147147

148-
### Translation
148+
### Translations and i18n
149149

150-
We currently have a framework for string translation in place that
151-
incorporates the `flutter_localizations` package and has some
152-
example usages.
153-
154-
For information on how the dart bindings are generated and how
155-
to add new strings, refer to the [translation docs](docs/translation.md).
150+
When adding new strings in the UI, we set them up to be translated.
151+
For details on how to do this, see the [translation doc](docs/translation.md).
156152

157153

158154
## License

assets/l10n/app_en.arb

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@
99
},
1010
"aboutPageOpenSourceLicenses": "Open-source licenses",
1111
"@aboutPageOpenSourceLicenses": {
12-
"description": "Section heading in About Zulip page to navigate to Open-source Licenses page"
12+
"description": "Item title in About Zulip page to navigate to Licenses page"
1313
},
1414
"aboutPageTapToView": "Tap to view",
1515
"@aboutPageTapToView": {
16-
"description": "Button label in About Zulip page to navigate to Open-source Licenses page"
16+
"description": "Item subtitle in About Zulip page to navigate to Licenses page"
1717
},
1818
"chooseAccountPageTitle": "Choose account",
1919
"@chooseAccountPageTitle": {

docs/translation.md

+92-38
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,98 @@
11
# Translations
22

33
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+
56

67
## Current state
78

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+
1025

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
1727

18-
## Adding new strings
28+
### Adding a string to the translation database
1929

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.
2337

38+
For example, this entry describes a UI string
39+
named `profileButtonSendDirectMessage`
40+
which appears in English as "Send direct message":
2441
```
2542
"profileButtonSendDirectMessage": "Send direct message",
2643
"@profileButtonSendDirectMessage": {
2744
"description": "Label for button in profile screen to navigate to DMs with the shown user."
2845
},
2946
```
3047

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`.)
3453

35-
Untranslated strings will be included in a generated
36-
`build/untranslated_messages.json` file. This output
37-
awaits #276.
3854

39-
## Using in code
55+
### Using a translated string in the code
4056

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:
4358
```
4459
import 'package:flutter_gen/gen_l10n/zulip_localizations.dart';
4560
```
4661

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:
4864
```
4965
Widget build(BuildContext context) {
5066
final zulipLocalizations = ZulipLocalizations.of(context);
5167
```
5268

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.
5582

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)`.
5885

59-
## Hack to enforce locale (for testing, etc)
6086

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:
6496

6597
```
6698
return GlobalStoreWidget(
@@ -75,16 +107,20 @@ return GlobalStoreWidget(
75107
home: const ChooseAccountPage()));
76108
```
77109

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+
81115

82116
## Tests
83117

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:
88124

89125
```
90126
await tester.pumpWidget(
@@ -95,3 +131,21 @@ localizations. Make sure to add the right
95131
supportedLocales: ZulipLocalizations.supportedLocales,
96132
home: PerAccountStoreWidget(
97133
```
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

Comments
 (0)