11# Translations
22
33Our 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```
4459import '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```
4965Widget 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```
6698return 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