• When working with the translation of your plugin I noted one incorrect use of the function _n().

    In https://plugins.trac.www.ads-software.com/browser/woocommerce-product-dependencies/trunk/woocommerce-product-dependencies.php#L461

    $owned_category_titles = WC_PD_Helpers::merge_category_titles( $owned_ids, 'and' );
    $owned_products_msg    = _n( 'a product', 'some products', sizeof( $owned_product_ids ), 'woocommerce-product-dependencies' );
    $owned_msg             = sprintf( _n( '%1$s from the %2$s category', '%1$s from the %2$s categories', sizeof( $owned_category_titles ), 'woocommerce-product-dependencies' ), $owned_products_msg, $owned_category_titles );

    _n() should be used only when the phrase contains a specific number, for instance:
    You have x apples in your basket.
    x e-mails have been deleted.

    Russian, as an example, the tranlator needs to write three (3) diffeerent translation whenever _n() is used.
    The first one (singular) is used for 1, 21, 31, 41… 101, etc.
    The second one (dual) is used for 2, 3, 4, 22, 23, 24… 102, 103, 104, etc.
    The third one (plural) is used for everything else.

    In other words, if you would happen to have 21 invalid form names, then a website that is configured to use Russian would present that phrase in singular!

    There are other languages that maintain 5 or even 6 different plural forms, all depending on which specific number you’ve got in your phrase.

    You can solve this in two ways.
    Either add the number, using printf ad _n() “You’ve got %d name that you… :”/”You’ve got %d names that you… :” followed by a list.
    Or just perform a simple test
    if d==1 then echo “The following name doesn’t work:” else echo “The follwing names don’t work:”…

Viewing 5 replies - 1 through 5 (of 5 total)
  • Plugin Author SomewhereWarm

    (@somewherewarm)

    Hey @tobifjellner,

    Thanks for the tip!

    So from what I can tell _n should not be used to handle the “1” case at all?

    But if _n only has 2 arguments, then how do translators handle languages with multiple plural forms, e.g. different forms for 2, 3 and >3?

    Cheers,
    Manos

    • This reply was modified 6 years, 9 months ago by SomewhereWarm.

    In your code you give a singular and a plural form, since that is what is needed when you run the code without translating the output. I.e. that is what is used for US English.

    The parameters for the target language will specify how many different versions will be needed of the translation.
    Let’s have a look at the translation interface for one of your phrases (“a product”//”some products”):

    Swedish has the same structure as English, here you get what you expect – one singular and one plural string to translate:
    https://translate.www.ads-software.com/projects/wp-plugins/woocommerce-product-dependencies/dev/sv/default?filters%5Boriginal_id%5D=5607296

    Japanese doesn’t change anything in the string, regardless of the number. As if you in English would be saying 1 beer, 2 beer, 3 beer… So there is only ONE box for the translation. Obviously, they would still translate “a product” and “some products” differently, though:
    https://translate.www.ads-software.com/projects/wp-plugins/woocommerce-product-dependencies/dev/ja/default?filters%5Boriginal_id%5D=5607296

    Maltese can have 5 (five) different forms of a translation, depending on which specific number is in the string. So your pair of “a product”/”some products” is suddenly split into five different boxes. Note that some example number values are posted next to each translation box, as a guide to the translator:
    https://translate.www.ads-software.com/projects/wp-plugins/woocommerce-product-dependencies/dev/mlt/default?filters%5Boriginal_id%5D=5607296

    In short: If you want to split between “a product” and “some products”, then you should simply treat this as two separate strings.

    Now, I realize that there may be one more, and even bigger problem with how you handle strings. I don’t even use WooCommerce anywhere, and haven’t analyzed your code that closely, but the fact that you have many short strings that begin with lowercase hint that you’re concatenating (gluing together) different parts of strings to form “a full sentence”.

    This is a big no-no, since not all languages have the same structure as English.
    German, for example, usually put the verb at the end. Or they may put a negating word in different places of a phrase. Or they may use a totally different verb to negate the phrase.

    Here’s a link to a good article around these issues, written by our famous Otto. https://ottopress.com/2012/internationalization-youre-probably-doing-it-wrong/

    Finally, thanks for being a good contributor to WordPress. Thanks for your willingness to grasp these things. Let me know (by answering here or by @mentioning me) if you have any questions for me.

    Plugin Author SomewhereWarm

    (@somewherewarm)

    Hey @tobifjellner

    Thanks for the detailed explanation!

    Seeing that _n provides multiple translation fields to account for multiple plural forms, it still seems to me that a translator can decide (depending on context) whether the English-equivalent singular should (or should not) be used in other plural forms or not.

    From what I can tell the main issue of using _n in this context arises in languages like Japanese, where the translator only gets 1 field, when 2 would be required.

    If so, then it might be a good idea to revise the documentation supporting _n: “Used when you want to use the appropriate form of a string based on whether a number is singular or plural.”

    This description far from conveys the depth of information you are sharing here. Remember that a sentence can contain multiple singular/plural forms for nouns / pronouns, and a language that uses the same form for nouns may potentially use different forms for pronouns, e.g.:

    “I bought 1 apple. It is one of the sweetest tasting apples I’ve had”.
    “I bought X apples. They are some of the sweetest tasting apples I’ve had.”

    In this case the first sentence might be a good candidate for _n, but not the second one? Which means that putting them together in one _n string would be bad practice.

    Assuming that the two sentences need to be fed into different gettext functions, then developers will need to concatenate them as well ??

    To account for RTL languages, the concatenation would need to happen via a gettext replacement — which is the only valid way to concatenate strings IMO.

    Which brings me to your second point — concatenation. Is there another valid way to put together two sentences than to replace one in the other, or to use an in-between string such as %1$s%2$s, 'some context' to handle the replacement in an RTL-safe manner? As long as you concatenate full sentences this should be a reasonably safe practice.

    Generally speaking, as much as we all try to follow good practices when writing code, in some cases it is very difficult to follow translation guidelines to the letter, because:

    1) Developers apparently must have a basic understanding of grammar and syntax in multiple languages.
    2) Very often it is impossible to structure code in a translation-aware way that accounts for every possible language.

    As an example — if your objective as a developer is to display a message containing some string replacements, and these strings are dependent on the state of multiple variables, then you can either choose to concatenate once, or construct your message in a dedicated block of code by writing potentially dozens/hundreds of different versions of the same string depending on your input variables.

    For most developers, the intuitively “better” approach would be to break things up and concatenate once — not out of laziness, but because there’s no other feasible way.

    The extent to which a project should adhere to translation guidelines is a product-related decision with serious implications from a development/cost perspective and as such, it needs to be weighed on a cost/benefit scale.

    For open-source plugins specifically, it is always up to the community itself to take initiative in implementing code changes to make popular plugins easier to translate — and as a result more accessible.

    • This reply was modified 6 years, 9 months ago by SomewhereWarm.

    Hi again.
    For the case “I bought 1 apple. It is one of the sweetest tasting apples I’ve had”.
    I’d say that should be handled as two separate strings.
    _n() goes perfectly with the first sentence.
    For the second sentence, I’d suggest that you in the code split between x == 1 and everything else.
    if x == 1 then _e('It is one of...',etc) else _e('They are ...', etc)

    Don’t bother too much about RTL. What comes first in the code will come first in presentation. I just happens to be that in RTL presentation starts from the right. __('first sentence').' '.__(second sentence') (I’m using some kind of pseudocode in my examples. I’m not that strong at PHP…)

    For the translator to do their work well, they usually need full sentences. In some cases this will raise the number of strings. But it may be needed. Different languages have totally different ways of linking different parts of a sentence together, and English is one of the simpler languages in this regard.
    At the same time, if you want to create a list of different parts, then you may need to change your format a bit. For instance, building a sentence like “You’ve got 5 apples, 1 pear and 2 oranges in your cart.” may be tricky. You could get closer to a solution by creating an outer phrase: “You’ve got %s in your cart.” and then inject the list that you build separately. But even here, you’d stumble on a problem:
    “1 pear” would be translated differently in Russian depending on which function this fraction plays in the sentence. In “You eat one pear”, one pear will be translated as “одну грушу” (being a direct object) where as simply “One pear” translates to “Одна груша”.

    Plugin Author SomewhereWarm

    (@somewherewarm)

    Hey @tobifjellner,

    Yup, good examples ?? When faced with the need to construct a complex string that requires 100s lines of code to generate, it’s very likely that a developer will not have the knowledge or capacity to do it right.

    In our case — some of the validation messages generated by Product Dependencies v1.3 may not be translatable in every language due to concatenation. However, the plugin allows store owners to define their own custom messages: These override the message generated by the plugin and could potentially be a valid workaround.

    At the moment we don’t have the capacity to circle back here and rewrite this in a more translation-proof manner, but we’ll definitely try to learn more on the subject and apply your suggestions when the time comes to return to this code.

    If anyone else is reading this — feel free to suggest code improvements that could have a positive contribution at https://github.com/somewherewarm/woocommerce-product-dependencies

    Thanks @tobifjellner ?? Closing this for now!

Viewing 5 replies - 1 through 5 (of 5 total)
  • The topic ‘Incorrect use of _n()’ is closed to new replies.