From d206ee91242c74601f879e9a3a351fe72976bbf2 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 20 Dec 2011 19:35:21 +0100 Subject: [PATCH] [ADD] guide to translations in OpenERP Web bzr revid: xmo@openerp.com-20111220183521-oo4dvofca1uqmzuy --- addons/web/static/src/js/view_list.js | 13 ++- doc/source/addons.rst | 122 ++++++++++++++++++++++++++ 2 files changed, 132 insertions(+), 3 deletions(-) diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js index f3369c4053b..73f9beb38aa 100644 --- a/addons/web/static/src/js/view_list.js +++ b/addons/web/static/src/js/view_list.js @@ -302,7 +302,11 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# last = first + limit; } this.$element.find('span.oe-pager-state').empty().text(_.str.sprintf( - "[%d to %d] of %d", first + 1, last, total)); + _t("[%(first_record)d to %(last_record)d] of %(records_count)d"), { + first_record: first + 1, + last_record: last, + records_count: total + })); this.$element .find('button[data-pager-action=first], button[data-pager-action=previous]') @@ -463,7 +467,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# })); this.do_push_state({ page: this.page, - limit: this._limit, + limit: this._limit }); return reloaded.promise(); }, @@ -1287,7 +1291,10 @@ openerp.web.ListView.Groups = openerp.web.Class.extend( /** @lends openerp.web.L var pages = Math.ceil(dataset.ids.length / limit); self.$row .find('.oe-pager-state') - .text(_.str.sprintf('%d/%d', page + 1, pages)) + .text(_.str.sprintf(_t("%(page)d/%(page_count)d"), { + page: page + 1, + page_count: pages + })) .end() .find('button[data-pager-action=previous]') .attr('disabled', page === 0) diff --git a/doc/source/addons.rst b/doc/source/addons.rst index 5bd2870baa8..ac264758d2b 100644 --- a/doc/source/addons.rst +++ b/doc/source/addons.rst @@ -295,6 +295,124 @@ to help you get started: guides/client-action guides/sidebar-protocol +Translations +------------ + +OpenERP Web should provide most of the tools needed to correctly translate your +addons via the tool of your choice (OpenERP itself uses `Launchpad's own +translation tool`_. + +Making strings translatable ++++++++++++++++++++++++++++ + +QWeb +~~~~ + +QWeb automatically marks all text nodes (any text which is not in an XML +attribute and not part of an XML tag) as translatable, and handles the +replacement for you. There is nothing special to do to mark template text as +translatable + +JavaScript +~~~~~~~~~~ + +OpenERP Web provides two functions to translate human-readable strings in +javascript code. These functions should be "imported" in your module by +aliasing them to their bare name: + +.. code-block:: javascript + + var _t = openerp.web._t, + _tl = openerp.web._tl; + +importing those functions under any other name is not guaranteed to work. + +.. note:: only import them if necessary, and only the necessary one(s), no need + to clutter your module's namespace for nothing + +.. js:function:: openerp.web._t(s) + + Base translation function, eager, works much like :manpage:`gettext(3)` + + :type s: String + :rtype: String + +.. js:function:: openerp.web._lt(s) + + Lazy equivalent to :js:func:`~openerp.web._t`, this function will postpone + fetching the translation to its argument until the last possible moment. + + To use in contexts evaluated before the translation database can be + fetched, usually your module's toplevel and the attributes of classes + defined in it (class attributes, not instance attributes set in the + constructor). + + :type s: String + :rtype: LazyString + +Text formatting & translations +"""""""""""""""""""""""""""""" + +A difficulty when translating is integrating data (from the code) into the +translated string. In OpenERP Web addons, this should be done by wrapping the +text to translate in an :manpage:`sprintf(3)` call. For OpenERP Web, +:manpage:`sprintf(3)` is provided by `underscore.string +`_. + +As much as possible, you should use the "named argument" form of sprintf: + +.. code-block:: javascript + + var translated_string = _.str.sprintf( + _t("[%(first_record)d to %(last_record)d] of %(records_count)d"), { + first_record: first + 1, + last_record: last, + records_count: total + })); + +named arguments make the string to translate much clearer for translators, and +allows them to "move" sections around based on the requirements of their +language (not all language order text like english). + +Named arguments are specified using the following pattern: ``%($name)$type`` +where + +``$name`` + the name of the argument, this is the key in the object/dictionary provided + as second parameter to ``sprintf`` +``$type`` + a type/format specifier, `see the list for all possible types + `_. + +.. note:: positional arguments are acceptable if the translated string has + *a single* argument and its content is easy to guess from the text + around it. Named arguments should still be preferred. + +.. warning:: you should *never* use string concatenation as it robs the + translator of context and make result in a completely incorrect + translation + +Extracting strings +~~~~~~~~~~~~~~~~~~ + +.. program:: gen_translations.sh + +Once strings have been marked for translation, they need to be extracted into +:abbr:`POT (Portable Object Template)` files, from which most translation tools +can build a database. + +This can be done via the provided :program:`gen_translations.sh`. + +It can be called either as :option:`gen_translations.sh -a` or by providing +two parameters, a path to the addons and the complete path in which to put the +extracted POT file. + +.. option:: -a + + Extracts translations from all standard OpenERP Web addons (addons bundled + with OpenERP Web itself) and puts the extracted templates into the right + directory for `Rosetta`_ to handle them + Utility behaviors ----------------- @@ -436,3 +554,7 @@ Python .. _.insertBefore(): http://api.jquery.com/insertBefore/ + +.. _Rosetta: +.. _Launchpad's own translation tool: + https://help.launchpad.net/Translations