stasm's comments

stasm | 2 years ago | on: The Wikimedia Foundation joins Mastodon and the Fediverse

> They own the domain.

It seems like a wasted opportunity to set up a new domain (in this case: wikimedia.social) rather than use an existing one with a subdomain, e.g. social.wikimedia.org or social.wikimediafoundation.org. With a new domain I still have to do the work to verify whether the domain is indeed owned by the Wikimedia Foundation.

stasm | 3 years ago | on: JavaScript and TypeScript features of the last 3 years

> - # private... not sure why they didn't just use the "private" keyword, but I don't care. I almost always use TypeScript anyways

One of the reasons was to allow private and public fields of the same name, so that subclasses are free to add own public fields without accidentally discovering private fields of superclasses. There were many more considerations that went into the design: https://github.com/tc39/proposal-class-fields/blob/main/PRIV....

There was a heated debate about this and the choice of the # sigil back in 2015 at the time private fields were being designed: https://github.com/tc39/proposal-private-fields/issues/14.

stasm | 3 years ago | on: Get in zoomer, we're saving React

> When you rename or move a file that you're editing, its window instantly reflects the new name and location.

How does macOS achieve its reactivity? Is it two-way bindings? Events/messages? Immediate mode re-renders?

stasm | 3 years ago | on: Ask HN: Does anybody still use bookmarking services?

If it's something I want to read and then be done with it, I keep the tab open.

If it's something I may want to come back to, or something that I'd be sad if it disappeared from the web, I save it to a synced folder using the SingleFile extension which inlines all content into a single HTML file.

stasm | 4 years ago | on: First look: adding type annotations to JavaScript

Type inference is one good reason. Similar syntax exists in a few modern languages, too: e.g. Rust and Swift. In languages with type inference it can be convenient that type annotations are in the postfix position relative to variable names:

    let user = new User();
    let user: User = new User();

stasm | 7 years ago | on: Fluent 1.0: a localization system for natural-sounding translations

> The second option shouldn't need to match as it is the default value anyway (signified by the *)

That's an interesting suggestion! Right now, the identifier between the brackets is required, but we could relax this in the future. In 1.0, we erred on the side of more conservative and explicit design, to improve the readability and discoverability of the syntax for translators.

stasm | 7 years ago | on: Fluent 1.0: a localization system for natural-sounding translations

The (2..4) range would work for Czech in this example (if I'm reading the CLDR right), but I'm afraid it wouldn't be sufficient for languages with more complex plural rules. Take the rule that returns "one" in Latvian, for example:

    n % 10 = 1 and
      n % 100 != 11 or
    v = 2 and
      f % 10 = 1 and
      f % 100 != 11 or
    v != 2 and
      f % 10 = 1
…where n is the absolute value of the number, f is the visible fractional digits with trailing zeros, and v is _the number_ of visible fraction digits with trailing zeros. Some rules can get even more complex than that; see [0] and [1].

It's safer and more robust to rely on the plural categories defined by the Unicode: (zero | one | two | few | many | other), and by the APIs provided by the platform (ICU, Intl.PluralRules, etc.).

[0] http://www.unicode.org/cldr/charts/latest/supplemental/langu...

[1] http://unicode.org/reports/tr35/tr35-numbers.html#Operands

stasm | 7 years ago | on: Fluent 1.0: a localization system for natural-sounding translations

You can get decent highlighting for basic Fluent messages by setting the editor to a mode for Properties files. For instance, whenever I type Fluent examples in GitHub, I use the following markdown:

    ```properties
    # A comment
    hello = Hello, world!
    ```
This is in fact by design. Properties files are quite nice for simple things. Fluent builds on top of them, and provides modern features like multiline text blocks (as long as it's indented, it's considered text continuation) and the micro-syntax for expressions: {$var}, {$var -> ...} etc.

So far, I haven't had much time to invest in building proper highlighting modes for popular editors. There are ACE and Vim modes mentioned in other comments here, and also a slightly outdated https://atom.io/packages/language-ftl20n written by a contributor. I'd love to see more such contributions, and I'll be more than happy to help by reviewing code!

stasm | 7 years ago | on: Fluent 1.0: a localization system for natural-sounding translations

It's up to the localizer to define variants corresponding to the language's plural categories. For Russian, that's (one, few, many). Interestingly, this particular example could simplified to (few, *), because "раз" is good for both 1, 5, 11, 55, etc. See https://projectfluent.org/play/?id=7d22f87c04b23b86d9f9149d5... for an example of this in action.

Authoring tools can help here, too. Pontoon, Mozilla's translation management system, pre-populates plural variants based on the number of plural categories defined in Unicode's CLDR.

stasm | 7 years ago | on: Fluent 1.0: a localization system for natural-sounding translations

Thanks!

> And it would result in combinatorial explosion of translation message versions if source code had to add special case for each quirk in each language.

This is the exact problem we designed Fluent to solve. If you get a chance to try it out, feel free to reach out to me if you questions. I'll be more than happy to help and to hear feedback.

stasm | 7 years ago | on: Fluent 1.0: a localization system for natural-sounding translations

Our goal was to design a simple DSL which is easy to read and make small edits to. Copying and pasting is a powerful learning method :)

We're also working on creating richer and more streamlined authoring experience in Pontoon, Mozilla's translation management system. You can read about the current state of Fluent support in Pontoon in my colleague's post at https://blog.mozilla.org/l10n/2019/04/11/implementing-fluent....

stasm | 7 years ago | on: Fluent 1.0: a localization system for natural-sounding translations

When Fluent formats translations, it returns simple strings (in the sense of primitive computer types). They can include markup which is parsed by a higher-level abstraction responsible for actually showing the translations somewhere in the UI. Take a look at https://github.com/projectfluent/fluent.js/wiki/DOM-Overlays in the experimental fluent-dom package, and their React equivalent, https://github.com/projectfluent/fluent.js/wiki/React-Overla....

stasm | 7 years ago | on: Fluent 1.0: a localization system for natural-sounding translations

It's possible to build this message in Fluent with nested selectors, or with adjacent selectors. I built an example using Polish, since that's a language I know best. (To be 100% correct in Polish, I'd need to use a different possessive pronoun, but doing so would actually remove the double use of gender from your question.)

https://projectfluent.org/play/?id=2d7ab4b7ed1c4d9656475614f...

It's a complex piece of UI and consequently, the resulting Fluent message is also quite complex. But possible to build :)

stasm | 7 years ago | on: Fluent 1.0: a localization system for natural-sounding translations

This is an excellent question and a very good use-case. I'm a Polish speaker myself, so I can definitely relate. It's also a good excuse for me to talk a little bit more about the advanced features of Fluent.

The Fluent Syntax is a simple declarative DSL. By design, it doesn't allow translators to build complex conditionals or use arithmetic. There is, however, an escape hatch. The problem you described can be solved in Fluent with a little bit of one-time help from the developer of the source code, through a feature of Fluent called custom functions.

Translations in Fluent can use functions to format values or decide between variants. There exist built-in functions like NUMBER and DATETIME. They are rarely used because the Fluent runtime calls them on numeric and temporal values implicitly, but they can be helpful when localizers wish to use custom formatting options.

    weekday-today = Today is {DATETIME($today, weekday: "long")}.
See https://projectfluent.org/play/?id=a3540d4f02c104a634adbfc0e... for a live example of DATETIME.

There can also be custom functions, defined during the initialization of the runtime. In Firefox, we use one such function called PLATFORM: https://searchfox.org/mozilla-central/rev/d33d470140ce3f9426.... It can be used as follows:

    open-preferences = {PLATFORM() ->  
        [windows] Open Options
       *[other] Open Preferences
    }
The logic of custom functions is entirely up to developers and the localization needs of the UI. In https://github.com/projectfluent/fluent/issues/228#issuecomm..., for instance, I suggested using a custom function to handle negative and positive floor numbers.

A custom function can also cater to the use-case you described. A simple and possibly naive implementation in JavaScript could look like the following one:

    function NUMBER_HEAD(num) {
        while (num > 999) {
            num /= 1e3;
        }
        let first = num.toString()[0];
        return num < 10 ? first
            : num < 100 ? first + "x"
            : first + "xx";
    }
I wrote this with Polish in mind, but it could be useful to other languages in which numerals are named after the first thousand-triple, in a left to right order. Depending on the exact product requirements, the function could be called NUMBER_HEAD_POLISH, or perhaps NUMBER_HEAD_TRIPLE_FIRST_DIGIT :)

Once defined, the function can be used as follows:

    # The Polish copy can take advantage of the custom function.
    page-of = {NUMBER_HEAD($pageTotal) ->
        [1xx] Strona {$pageCurrent} ze {$pageTotal}
       *[other] Strona {$pageCurrent} z {$pageTotal}
    }
This method still requires some work from developers, but it only needs to happen once and in a single palce in code: where the Fluent runtime is initialized. Because they are code, custom functions can be reviewed and tested just as any other code in the code base, to help ensure that they do what they claim to :)

Importantly, the use of the custom function is completely opt-in.

    # The English copy doesn't need any special handling.
    page-of = Page {$pageCurrent} of {$pageTotal}
All localization callsites remain unchanged, and all existing translations remain functional.

stasm | 7 years ago | on: Fluent 1.0: a localization system for natural-sounding translations

This is a great point and something that we've seen come up very often in building UIs. The good practice which we recommend to developers at Mozilla is to avoid splitting or nesting messages, because it makes it harder for translators to see the entire translation at once.

We've taken a layered approach to designing Fluent: what we're announcing today is the 1.0 of the syntax and file format specification. The implementations are still maturing towards 1.0 quality, but let me quickly describe what our current thinking is.

For JavaScript, we're working on low-level library which implements a parser of Fluent files, and offers an agnostic API for formatting translations. On top of it we hope to see an ecosystem of glue-code libraries, or bindings, each satisfying the needs of a different use-case or framework.

I've been working on one such binding library called fluent-react. It's still in its 0.x days, but it's already used in a number of Mozilla projects (e.g. in Firefox DevTools). In fluent-react translations can contain limited markup. During rendering, the markup is sanitized and then matched against props defined by the developer in the source code, in a way that overlays the translation onto the source's structure. Hence, this feature is called Overlays. See https://github.com/projectfluent/fluent.js/wiki/React-Overla....

Here's how you could re-implement your example using fluent-react. Note that the <a>'s href is only defined in the prop to the Localized component.

    <Localized
        id="confirm"
        $clickCount={7}
        a={<a href="..."></a>}
    >
        {"Please <a>click here {$clickCount ->
            [one] 1 time
           *[other] {$clickCount} times
        }</a> to confirm."}
    </Localized>
I'd love to get more feedback on ideas in fluent-react. Please feel free to reach out if you have more questions!

stasm | 7 years ago | on: Fluent 1.0: a localization system for natural-sounding translations

(Author of the blog post here.) Great question, thanks! Unicode defines six categories of plural forms: zero, one, two, few, many, and other. The names of these categories always appear in English. Unicode also maintains a collection of all mappings of numerical rules to these categories, for all languages supported by the CLDR. See http://www.unicode.org/cldr/charts/latest/supplemental/langu... for the mapping corresponding to the Czech grammar.
page 1