tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

review.rst (12645B)


      1 .. role:: bash(code)
      2   :language: bash
      3 
      4 .. role:: js(code)
      5   :language: javascript
      6 
      7 ===============================
      8 Guidelines for Fluent Reviewers
      9 ===============================
     10 
     11 This document is intended as a guideline for developers and reviewers when
     12 working with FTL (Fluent) files. As such, it’s not meant to replace the
     13 `existing extensive documentation`__ about Fluent.
     14 
     15 __ ./tutorial.html
     16 
     17 `Herald`_ is used to set the group `fluent-reviewers`_ as blocking reviewer for
     18 any patch modifying FTL files committed to Phabricator. The person from this
     19 group performing the review will have to manually set other reviewers as
     20 blocking, if the original developer didn’t originally do it.
     21 
     22 
     23 .. hint::
     24 
     25  In case of doubt, you should always reach out to the l10n team for
     26  clarifications.
     27 
     28 
     29 Message Identifiers
     30 ===================
     31 
     32 While in Fluent it’s possible to use both lowercase and uppercase characters in
     33 message identifiers, the naming convention in Gecko is to use lowercase and
     34 hyphens (*kebab-case*), avoiding CamelCase and underscores. For example,
     35 :js:`allow-button` should be preferred to :js:`allow_button` or
     36 :js:`allowButton`, unless there are technically constraints – like identifiers
     37 generated at run-time from external sources – that make this impractical.
     38 
     39 When importing multiple FTL files, all messages share the same scope in the
     40 Fluent bundle. For that reason, it’s suggested to add scope to the message
     41 identifier itself: using :js:`cancel` as an identifier increases the chances of
     42 having a conflict, :js:`save-dialog-cancel-button` would make it less likely.
     43 
     44 Message identifiers are also used as the ultimate fall back in case of run-time
     45 errors. Having a descriptive message ID would make such fall back more useful
     46 for the user.
     47 
     48 Avoid Generating Identifiers at Run-time
     49 ----------------------------------------
     50 
     51 Generating identifiers at run-time relying on variables should be avoided unless
     52 the number of strings to manage is particularly large.
     53 
     54 Consider for example these Fluent strings and associated JavaScript code:
     55 
     56 .. code-block:: fluent
     57 
     58  bar-tooltip-social = Social Media Trackers
     59  bar-tooltip-cookie = Cross-Site Tracking Cookies
     60  bar-tooltip-tracker = Tracking Content
     61  bar-tooltip-fingerprinter = Fingerprinters
     62  bar-tooltip-cryptominer = Cryptominers
     63 
     64 
     65 .. code-block:: javascript
     66 
     67  document.l10n.setAttributes(div, `bar-tooltip-${type}`);
     68 
     69 
     70 From a developer perspective, this code is clean and intuitive, but it creates
     71 two distinct problems:
     72 
     73 - Updating one of the strings in the group requires either updating all of them,
     74  forcing retranslation, or updating the code logic to support exceptions.
     75 - Finding unused strings in the codebase becomes impossible, as the message
     76  identifier is never spelled out.
     77 
     78 Potentially, the code above can also lead to generating a missing identifiers,
     79 while that's harder with an explicit list.
     80 
     81 An explicit map or object is the preferred approach to deal with these cases:
     82 
     83 
     84 .. code-block:: javascript
     85 
     86  const messageIDs = {
     87    social: "bar-tooltip-social",
     88    cookie: "bar-tooltip-cookie",
     89    tracker: "bar-tooltip-tracker",
     90    cryptominer: "bar-tooltip-cryptominer",
     91    fingerprinter: "bar-tooltip-fingerprinter",
     92  };
     93  document.l10n.setAttributes(div, messageIDs[type]);
     94 
     95 
     96 Comments
     97 ========
     98 
     99 When a message includes placeables (variables), there should always be a
    100 comment explaining the format of the variable, and what kind of content it will
    101 be replaced with. This is the format suggested for such comments:
    102 
    103 
    104 .. code-block:: fluent
    105 
    106  # This string is used on a new line below the add-on name
    107  # Variables:
    108  #   $name (String) - Add-on author name
    109  cfr-doorhanger-extension-author = by { $name }
    110 
    111 
    112 By default, a comment is bound to the message immediately following it. Fluent
    113 supports both `file-level and group-level comments`__. Be aware that a group
    114 comment will apply to all messages following that comment until the end of the
    115 file. If that shouldn’t be the case, you’ll need to “reset” the group comment,
    116 by adding an empty one (:js:`##`), or moving the section of messages at the end
    117 of the file.
    118 
    119 __ https://projectfluent.org/fluent/guide/comments.html
    120 
    121 Comments are fundamental for localizers, since they don’t see the file as a
    122 whole, or changes as a fragment of a larger patch. Their work happens on a
    123 message at a time, and the context is only provided by comments.
    124 
    125 License headers are standalone comments, that is, a single :js:`#` as prefix,
    126 and the comment is followed by at least one empty line.
    127 
    128 Changes to Existing Messages
    129 ============================
    130 
    131 You must update the message identifier if:
    132 
    133 - The meaning of the sentence has changed.
    134 - You’re changing the morphology of the message, by adding or removing attributes.
    135 
    136 Messages are identified in the entire localization toolchain by their ID. For
    137 this reason, there’s no need to change attribute names.
    138 
    139 If your changes are relevant only for English — for example, to correct a
    140 typographical error or to make letter case consistent — then there is generally
    141 no need to update the message identifier.
    142 
    143 There is a grey area between needing a new ID or not. In some cases, it will be
    144 necessary to look at all the existing translations to determine if a new ID
    145 would be beneficial. You should always reach out to the l10n team in case of
    146 doubt.
    147 
    148 Changing the message ID will invalidate the existing translation, the new
    149 message will be reported as missing in all tools, and localizers will have to
    150 retranslate it. This is the only reliable method to ensure that localizers
    151 update existing localizations, and run-time stop using obsolete translations.
    152 
    153 You must also update all instances where that message identifier is used in the
    154 source code, including localization comments.
    155 
    156 Non-text Elements in Messages
    157 =============================
    158 
    159 When a message includes non text-elements – like anchors or images – make sure
    160 that they have a :js:`data-l10n-name` associated to them. Additional
    161 attributes, like the URL for an anchor or CSS classes, should not be exposed
    162 for localization in the FTL file. More details can be found in `this page`__
    163 dedicated to DOM overlays.
    164 
    165 __ https://github.com/projectfluent/fluent.js/wiki/DOM-Overlays#text-level-elements
    166 
    167 This information is not relevant if your code is using `fluent-react`_, where
    168 DOM overlays `work differently`__.
    169 
    170 __ https://github.com/projectfluent/fluent.js/wiki/React-Overlays
    171 
    172 Message References
    173 ==================
    174 
    175 Consider the following example:
    176 
    177 
    178 .. code-block:: fluent
    179 
    180  newtab-search-box-search-the-web-text = Search the Web
    181  newtab-search-box-search-the-web-input =
    182      .placeholder = { newtab-search-box-search-the-web-text }
    183      .title = { newtab-search-box-search-the-web-text }
    184 
    185 
    186 This might seem to reduce the work for localizers, but it actually doesn’t
    187 help:
    188 
    189 - A change to the referenced message (:js:`newtab-search-box-search-the-web-text`)
    190  would require a new ID also for all messages referencing it.
    191 - Translation memory can help with matching text, not with message references.
    192 
    193 On the other hand, this approach is helpful if, for example, you want to
    194 reference another element of the UI in your message:
    195 
    196 
    197 .. code-block:: fluent
    198 
    199  help-button = Help
    200  help-explanation = Click the { help-button } to access support
    201 
    202 
    203 This enforces consistency and, if :js:`help-button` changes, all other messages
    204 will need to be updated anyway.
    205 
    206 Terms
    207 =====
    208 
    209 Fluent supports a specific type of message, called `term`_. Terms are similar
    210 to regular messages but they can only be used as references in other messages.
    211 They are best used to define vocabulary and glossary items which can be used
    212 consistently across the localization of the entire product.
    213 
    214 Terms are typically used for brand names, like :js:`Firefox` or :js:`Mozilla`:
    215 it allows to have them in one easily identifiable place, and raise warnings
    216 when a localization is not using them. It helps enforcing consistency and brand
    217 protection. If you simply need to reference a message from another message, you
    218 don’t need a term: cross references between messages are allowed, but they
    219 should not be abused, as already described.
    220 
    221 Variants and plurals
    222 ====================
    223 
    224 Consider the following example:
    225 
    226 
    227 .. code-block:: fluent
    228 
    229  items-selected =
    230      { $num ->
    231          [0] Select items.
    232          [one] One item selected.
    233         *[other] { $num } items selected.
    234      }
    235 
    236 
    237 In this example, there’s no guarantee that all localizations will have this
    238 variant covered, since variants are private by design. The correct approach for
    239 the example would be to have a separate message for the :js:`0` case:
    240 
    241 
    242 .. code-block:: fluent
    243 
    244  # Separate messages which serve different purposes.
    245  items-select = Select items
    246  # The default variant works for all values of the selector.
    247  items-selected =
    248      { $num ->
    249          [one] One item selected.
    250         *[other] { $num } items selected.
    251      }
    252 
    253 
    254 As a rule of thumb:
    255 
    256 - Use variants only if the default variant makes sense for all possible values
    257  of the selector.
    258 - The code shouldn’t depend on the availability of a specific variant.
    259 
    260 More examples about selector and variant abuses can be found in `this wiki`__.
    261 
    262 __ https://github.com/projectfluent/fluent/wiki/Good-Practices-for-Developers#prefer-separate-messages-over-variants-for-ui-logic
    263 
    264 In general, also avoid putting a selector in the middle of a sentence, like in
    265 the example below:
    266 
    267 
    268 .. code-block:: fluent
    269 
    270  items-selected =
    271      { $num ->
    272          [one] One item.
    273         *[other] { $num } items
    274      } selected.
    275 
    276 
    277 :js:`1` should only be used in case you want to cover the literal number. If
    278 it’s a standard plural, you should use the :js:`one` category for singular.
    279 Also make sure to always pass the variable to these messages as a number, not
    280 as a string.
    281 
    282 Access Keys
    283 ===========
    284 
    285 The following is a simple potential example of an access key:
    286 
    287 .. code-block:: fluent
    288 
    289  example-menu-item =
    290      .label = Menu Item
    291      .accesskey = M
    292 
    293 Access keys are used in menus in order to help provide easy keyboard shortcut access. They
    294 are useful for both power users, and for users who have accessibility needs. It is
    295 helpful to first read the `Access keys`__ guide in the Windows Developer documentation,
    296 as it outlines the best practices for Windows applications.
    297 
    298 __ https://docs.microsoft.com/en-us/windows/uwp/design/input/access-keys
    299 
    300 There are some differences between operating systems. Linux mostly follows the same
    301 practices as Windows. However, macOS in general does not have good support for accesskeys,
    302 especially in menus.
    303 
    304 When choosing an access key, it's important that it's unique relative to the current level
    305 of UI. It's preferable to avoid letters with descending parts, such as :code:`g`,
    306 :code:`j`, :code:`p`, and :code:`q` as these will not be underlined nicely in Windows or
    307 Linux. Other problematic characters are ones which are narrow, such as :code:`l`,
    308 :code:`i` and :code:`I`. The underline may not be as visible as other letters in
    309 sans-serif fonts.
    310 
    311 Linter
    312 ======
    313 
    314 :bash:`mach lint` includes a :ref:`l10n linter <L10n>`, called :bash:`moz-l10n-lint`. It
    315 can be run locally by developers but also runs on Treeherder: in the Build
    316 Status section of the diff on Phabricator, open the Treeherder Jobs link and
    317 look for the :js:`l1nt` job.
    318 
    319 Besides displaying errors and warnings due to syntax errors, it’s particularly
    320 important because it also checks for message changes without new IDs, and
    321 conflicts with the firefox-l10n-source repository used to ship localized versions of
    322 Firefox.
    323 
    324 
    325 .. warning::
    326 
    327  Currently, there’s an `issue`__ preventing warnings to be displayed in
    328  Phabricator. Checks can be run locally using :bash:`./mach lint -l l10n -W`.
    329 
    330  __ https://github.com/mozilla/code-review/issues/32
    331 
    332 
    333 Migrating Strings From Legacy or Fluent Files
    334 =============================================
    335 
    336 If a patch is moving legacy strings (.properties, .DTD) to Fluent, it should
    337 also include a recipe to migrate existing strings to FTL messages. The same is
    338 applicable if a patch moves existing Fluent messages to a different file, or
    339 changes the morphology of existing messages without actual changes to the
    340 content.
    341 
    342 Documentation on how to write and test migration recipes is available in `this
    343 page`__.
    344 
    345 __ ./fluent_migrations.html
    346 
    347 
    348 .. _Herald: https://phabricator.services.mozilla.com/herald/
    349 .. _fluent-reviewers: https://phabricator.services.mozilla.com/tag/fluent-reviewers/
    350 .. _fluent-react: https://github.com/projectfluent/fluent.js/wiki/React-Bindings
    351 .. _term: https://projectfluent.org/fluent/guide/terms.html