Showing posts with label i18n. Show all posts
Showing posts with label i18n. Show all posts

Thursday, May 8, 2014

Loading JavaScript configuration and i18n for Plone add-ons

collective.jsconfiguration is a Plone package that want to give a possible approach to the way of including JavaScript configuration or 18n data inside add-ons.
Although it's heavily targeted to add-ons that contains JavaScript components it will not provide any JavaScript at all, but only three possible ways to get configuration from the server.

Motivations

In last years I saw a lot of different approach to the problem, looking at the source of many different add-ons; every approach could have some advantage or side effects. I've only one golder rule: the worst you can do is to generate your JavaScript dynamically from the server:
class JavaScriptView(BrowserView):

    def __call__(self, *args, **kwargs):

        registry = queryUtility(IRegistry)
        settings = registry.forInterface(IMyPackage)

        # ...omissis...

        self.request.response.setHeader('Content-Type', 'text/javascript')
        return """function something() {
   var settings_1 = %(settings_1)s;
   var settings_2 = %(settings_2)s;

   // do something

}
""" % {"settings_1": settings.settings_1,
       "settings_2": settings.settings_2}
This approach "works", but writing something in a programming language through the usage of another is bad and ugly:
  • A developer that's look to your JavaScript code can be astonished when he get that the code in inside a Python source.
  • The more JavaScript code became complex, the more will became unreadable/unmaintainable.
  • You can't use your favorite IDE JavaScript features because he can't understand you are (partially) write a JavaScript source
  • ... (another 10 "because is bad" can be probably found) ...
 So it's clear that the JavaScript must be in a pure .js file.

A (little) step forward can be to split the code above in a "configuration part" and keep the real code (that will read configuration) in another .js file.
This is better, but still you have some JavaScript code (even if simpler) in a Python/.zpt file... and you have 2 JavaScript instead of ones.

AJAX?

Yeah, AJAX is a right answer to the problem: you write a pure JavaScript code that ask to the server, that's normally reply with a JSON, all configuration needed. You have one .js source file and nothing more.

Well... to be honest you have an additional call to the server for getting the configuration, but how this can be a bad thing? It will probably be a very small piece of data, isn't it? So very very fast...
Not exactly.
The Web is full of articles about mobile/front-end development and how to keep high performance. If you want to read something interesting about this argument take a look to the recent "Is jQuery Too Big For Mobile?".
The real enemy is not how big is your data (hey, I'm not talking of a 10Mb HTML file!) but the latency: the network (even the mobile ones) it's quite fast nowadays but the latency is always pretty high (even more on mobile). We must reduce at the minimum the loading of external resources, especially if they are resources that can't be loaded asynchronously.

Going back to our dummy example above: it's clear that the configuration is a required resource for running the something JavaScript function. We can't start using it without having server side configuration (maybe we can do a little better with some advanced approach... I find this way to call a JavaSript library before it's loaded really interesting).

It will be the same also for i18n: we can't draw a user interface before we have the internationalized strings available.
I've some experience with jarn.jsi18n, and I used it in at least a couple of "pure JavaScript" Plone projects (projects where you don't have a template where add data in other ways I'll introduce later).
In one of them I found that the user interface were loaded in english at first access; after the first attempt translated messages were used normally.
This happened because strings were loaded through an AJAX call to the server but if you use the string "too early" (before AJAX response) they were still in primer language. Luckily after the first usage the library cache translations in the browser local storage, a very smart approach indeed.
The library gives no "onload" callback, so I was planning to provide a pull request for this but... you really like the idea? Delay the execution because we need the i18n strings?!

So AJAX is bad for i18n/configuration load?

No! If you are developing a pure JavaScript application and you don't know what backend technology will be used (if any) AJAX in probably the only choice.

But I'm focused on Plone add-ons here; we know what kind of backend we have: why not load configuration directly from the Plone pages, where your JavaScript will be executed?

Load configuration and i18n from templates

When you are facing the i18n problem with JavaScript and you have a template available (so you are developing an add-on with a view, or a viewlet) you can put translation in HTML 5 Data attributes.
This way you don't need any AJAX call and (even better) you can rely on i18ndude (I tried to use i18ndude also with jarn.jsi18n but it's not fully compatible, there's no support for the "default" value of a translation msgid) and zope.i18n machinery. Cool!

The same will be for general configuration stuff: you can put you server side data inside templates, again in HTML data 5 attributes. This is not a new technique, Plone 5 added some some information inside data attributes (and for what I saw also some small encoded JSON data).

But while using template and data attributes is amazing for translation, I don't like too much using this approach for other data like server side configuration: you will probably find yourself convert a lot of strings in other data types or, if the configuration is large, flood the page of HTML 5 data attributes.

Still use JSON

What is the best way to give data to a JavaScript developer? In my opinion the simplest and most direct way is still JavaScript itself, so providing a JavaScript object.
For example: Plone 4 (and also Plone 5) gives to JavaScript developers the portal_url variable that always gives you the URL of the site. Yes, this is ugly because this variable pollute the global namespace, if another piece of code define a global portal_url var one of them will be overwritten, ... I'm also sure that will change in future, probably it will became a new data attribute, but it's still the quicker way to read that information from JavaScript.

There is an old and well know convention on how not spawn global vars all around the global namespace: put them in a data structure: instead of calling "portal_url" a "plone.portal_url" could be a lot better.
However a lot of purists neither like this approach; in facts you are still polluting the global namespace and the possibility that another JavaScript code don't define a "plone" var is near to zero... but not zero (however I still like this, and I kept it in collective.jsconfiguration).

What's left is still a JSON data source, but we don't want to use AJAX. Uhmm...

The last chance: a lot of super-power JavaScript framework started to use client side templates.
The technique is really simple: define a new script tag of a type that the browser will not know and so it will not try to execute, and put inside it what you want.

You can use this script to store demi-HTML code, or other type of data... as a plain JSON

collective.jsconfiguration

This is the way used by collective.jsconfiguration: it simply register a new viewlet (in the page head) and wait for you registration of additional configuration (from 3rd party products).

Add-ons can register three different types of them:
  • type=text/collective.jsconfiguration.json
    It will store a JSON data inside the script tag. The source will be available to be executed by JSON.parse.
  • text/collective.jsconfiguration.xml
    It will store any kind of data, but it's designed to be used with demi-HTML, as a view/page template output. As the HTML is inside a script tag you are not really forced to use an XHTML or HTML 5 data attributes but you can simply provide an XML.
  • text/javascript
    This is exactly like the JSON case, but it's for guys that like the idea to provide their data in a pure JavaScript plain object.
It is especially useful if you are developing an add-on with JavaScript but without any server side rendering element (no template, no view, no viewlet, ...) because the JavaScript will find the configuration and translation you defined in the HTML head of your page: you only need to configure what to put inside.

plone.app.registry integration

When talking of configuration and user preferences you will probably store your ones inside the Plone registry. Using collective.jsconfiguration and collective.regjsonify you can store inside your JSON or plain JavaScript object the same data stored in your add-on registry sheet.

Example application

For better understanding how collective.jsconfiguration (and collective.regjsonify) works, you can check the example application.

OT: goodbye Plone (for a while)

This was the really-last product in my list of "Plone add-ons that I think could be cool or useful" so I decided to stop spending time on Plone development for a while.
To keep myself busy I will probably start learning some new technology, or I will try to finish my Plone Workflow book...
Who knows?

Monday, April 23, 2012

Form in Plone: a simple approach using collective.wtforms

If you follow the Plone-Developers mailing list, you probably already know about a recent thread called "Rewrite old cpt forms to new technology like z3cform". If not: let simply me say that it's talking about removing old Plone stuff, replacing it with the new-way of doing form in Plone: z3c.form.

Although this is a very interesting discussion (that can also help you understand how things works inside the community) the argument of my article came from a single comment of Nathan Van Gheem, that introduced to me collective.wtforms.
This is a Plone integration for a Python, framework independent, library that generate forms: WTForms.

WTForms in general
Using WTForms in Python seems really easy, as introduced in the Getting started section of the documentation. The concept behind are the same we already know from Zope and Plone libraries:
  • a schema definition
  • a set of field types
  • a set of widgets
As it is a Python only framework we don't find ZCA around us.

Using collective.wtforms
The collective.wtforms package is simple. The Plone integration seems a simple work. It only gives you a base WTFormView class (a Zope 3 view that easily integrated you form in the Plone layout) and a WTFormControlPanelView class (if you ever need a Plone control panel form).

That's it.

Inside the view definition you must then use the basical WTForm features. Let's see an example:
from wtforms import Form
from wtforms import TextField
from wtforms import validators
from collective.wtforms.views import WTFormView

class Form1(Form):
    one = TextField("Field One", [validators.required()])
    two = TextField("Field Two")
    three = TextField("Field Three")

class Form1View(WTFormView):
    formClass = Form1
    buttons = ('Create', _(u'Cancel'))
    #label = _(u'Form 1')

    def submit(self, button):
        if button == 'Create' and self.validate():
            # do fun stuff here
            self.context.value = self.form.one.data
Then you need a zcml registration:
  <browser:page
      name="form1"
      for="*"
      class=".forms.Form1View"
      permission="zope2.View"
  />
You can find the example above, and other discussed later, in the package example.wtforms.
Problems
Of course, as always, is simple making simple things.
I found mainly two problems: the widget layout and internationalization.

WTForms widget layout
The template definition done in collective.wtforms is enough to display a form in the Plone way, however when displaying the "real widget" code we are using the WTForms core features. In that case we sometimes see some strange HTML (I mean: strange for Plone users).

One example: when using RadioField fields, the form radio set in wrapped in a UL/LI HTML structure.

This is not a big problem, just I want to say that this is uncommon in Plone forms.
Obviously WTForms can be extended and supports custom widgets.

A bigger task: I18N
A bigger problem was internationalization. The current alpha version of collective.wtforms (1.0a3) doesn't support internationalization of the UI, however fixing this is simple (you can find my changes in a fork of the original project)

With small changes you can see a fully translated of:
  • form title
  • form general description
  • submit buttons
  • fields label
  • fields description
The main problem is that WTForms doesn't support any internationalization.

Recently they added a new i18n module that helps users to translate the internal label (like: the error message after you didn't provided a required field). However this is not usable out of the box in Plone, because Plone translation mechanism is not the basic Python ones.
I tested it adding an italian translation to WTForms (it was missing in the core, so I also provided it to authors and they quickly integrate it. Man: I really love open source!) and I see no difference.

So what I did is to integrate the native ".pot" translation file into the Plone environment and leave this translations to the Zope Page Template engine... and obviously it worked!

Then: I needed some other simple fixes (like: we can't directly render the WTForms label, but we need to use manually render it using TAL).

Again: the the fork for see some code.

Vocabularies
Translating the vocabulary labels for select, multiselect and radio fields was not so simple. WTForms simply want an iterable argument named choices.

What I was forced to do (better patterns are welcome) is to provide a VocabularyWrapper class where vocabulary labels are translated accessing directly the translation machinery.

Conclusions
I'm sure that we can find also other form library outside Zope (Deform can be another valid choice and also YAFOWIL), however I find the use of WTForms really simple and easy to learn.

Saturday, July 16, 2011

Hidden (great) secrets inside ATVocabularyManager

ATVocabularyManager is a well-know Plone product developed by BlueDynamics that make simple handling vocabulary values used by your contents directly inside Plone.
That's all: the power of the product is all in this first sentence.

Generic Setup Strike Back
In my personal experience I not commonly found projects that need a Plone user able to change vocabularies, but in the information architecture this is quite common (they call this "Controlled Vocabulary"). So I used ATVocabularyManager a couple of times in the past, but I never became habit to rely on it.

Another thing I don't liked was the unexistent Generic Setup integration.

Recently a customer asked us a new project with some new content types, with a lot of field with controlled vocabularies (with many values inside). Also he explicitly ask to be able to handle and change it in the future.

So I looked back to ATVocabularyManager, hoping that during this time something changed.

A New Hope
This time I note immediately that latest releases (1.5 branch for Plone 3.3, and 1.6 for Plone 4) give us something new.

First of all: for the first time I understand that the name prefix "AT" means obviously "Archetypes", but you can think it as "the way of controlling vocabulary is done using some archetypes contents". This mean that you can use it only if your vocabularies are inside archetypes content types? No!
You can also use it to handle whatever ZCML vocabulary you need (portlet? Dexterity?)

The other thing I found is that now we have Generic Setup integration. Great!

The Generic Setup integration
Right now the integration is not fully complete. Seems that export step is not there, but looking at the code I saw that the import step code (the most important!) is available. The product right now suffer only some missing of documentation.

How the import steps works? Instead of creating new vocabulary content types ("Simple Vocabulary", "Sorted Simple Vocabulary", ...), it is based on the "IMS VDEX Vocabulary File" content.

At first glance this can seem the less user friendly way and most obscure content type (and probably this is true) but going back to information architecture this is probably the best choice, because it's based on an XML international standard for handle vocabularies: the IMS VDEX.
Another good news: this format also support i18n (and also Plone)!

What I needed after this is simple: provide a VDEX compatible XML file. How? Let show a complete example.

How to add the GS support
First of all you need to provide a "vocabularies.xml file" to your profile directory.
The format of the file is as follow:

<?xml version="1.0"?>
<object name="portal_vocabularies" meta_type="ATVocabularyManager">
<object name="test.vdex" /> 
...
</object>

So you need to provide a reference to a vocabulary file for every vocabulary you need to import (.vdex of .xml file extensions are valid ones).

Where to put all vocabulary files? You need also to put at the same level a "vocabularies" directory. Inside this you simply need to put all files.

Now I will show the file format of our test.vdex file.

<vdex xmlns="http://www.imsglobal.org/xsd/imsvdex_v1p0"
orderSignificant="true">
  <vocabIdentifier>test-vocab</vocabIdentifier>
  <vocabName>
    <langstring language="en">A test vocabulary</langstring>
    <langstring language="it">Un vocabolario di test</langstring>
  </vocabName>
  <term>
    <termIdentifier>aaa</termIdentifier>
    <caption>
      <langstring language="en">A value</langstring>
      <langstring language="it">Un valore</langstring>
    </caption>
  </term>
  <term>
    <termIdentifier>bbb</termIdentifier>
    <caption>
      <langstring language="en">Another value</langstring>
      <langstring language="it">Un altro valore</langstring>
    </caption>
  </term>
</vdex>

That's all. We created a vocabulary with two entry inside (foo values are "aaa" and "bbb"). As say above, handle this directly from Plone is not very comfortable (simpler vocabulary type are easier to understand) however this is great for Generic Setup install step!

Internationalization note
A little bug on this approach. Seems that even if you plan to not provide an internationalization for you vocabularies (for example: you only need to provide your italian, spanish or something other translation) you still need to provide also the english one, or the vocabulary content inside ATVocabularyManager control panel will not show you the right title of the vocabulary (something like "unnamed vocabulary" instead of "Un vocabolario di test").
But you can use a trick and duplicate your locale specific translation also for english. Also, put english translation first.

Not very comfortable right now
If you still think it, you are right. Maybe that vdex is a well know standard for handle vocabulary, but build a vocabulary with this XML format can be not very simple.

For example, the customer give us a document (a MS Word attachment, obviously) with a set of lists of values. The easy way is to put all this in some CSV files, where columns are "italian translation" and "english translation".
But after that we need to convert this in the vdex format. How?

What I did is too look on the cheeseshop for a library that can convert a CSV in a VDEX file. What I find is vdexcsv!
Two funny thing about it:
  • It was released something like two hour before I performed that search!
  • The company behind this product is again BlueDynamics!
What this product does? You only need to easy_install it then you will be gifted with a new bash command: csv2vdex.

The documentation is clear: you need to provide a CSV file, some parameter, and you'll obtain your vdex file.

For the example above I used a CSV like this:

"key";"english";"italian"
"aaa";"A value";"Un valore"
"bbb";"Another value";"Un altro valore"

This was named test.csv.

Then I called the script in this way:
csv2vdex test-vocab 'A test vocabulary,Un vocabolario di test' test.csv test.vdex --languages en,it --startrow 1

One last step: the generated vdex file is perfect for all but the root node name. As in the example above you need to have the root node called vdex, but the script generate it as vocabulary. However after contacting Jens (the product creator, that also help me to reach this results) he was in agreement to change this in future releases of vdexcsv.
For now, simply rename the node!
EDIT (2011-08-22): the good guys released vdexcsv 1.1 that is now fully standard compliant, so no more needs to manually rename the node!

Saturday, June 25, 2011

Inno alla Collective

Qualche tempo fa mi è capitata una richiesta da parte di un cliente che al 70% si è rivelata già svolta grazie al lavoro di un'altra azienda (straniera) che produce prodotti Plone e che ha rilasciato il codice.
Fantastico!

Premessa: dato che il discorso che vorrei affrontare qui non è relativo a quel prodotto o a quell'azienda ma è più un discorso di attitudine generale, non scendo nei particolari. Chiamiamoli quindi Prodotto X e Azienda X (e visto che ci siamo, anche Cliente X).

Come innumerevoli altre volte, con soddisfazione ho potuto dire al Cliente X "quello che mi chiedi, Plone (la comunità) te lo offre praticamente già fatto". Soddisfazione sua (che ancora una volta ha capito quanto abbia fatto bene a scegliere Plone nel panorama dei CMS Open Source), soddisfazione mia (che lavoro meno e non devo per l'ennesima volta reinventare la ruota).

Rimane fuori quel piccolo 30%. Il Prodotto X era carente di:
  • traduzione italiana (questo capita ancora troppo spesso)
  • qualche funzionalità aggiuntiva, specifica del Cliente X, che poteva però essere resa una funzionalità generica e riutilizzabile per tutto il Prodotto X.
Aggiungiamo anche un piccolo particolare, ossia che il prodotto era testato (funzionante) solo per Plone 4. Il Cliente X ha una solida installazione Plone 3.3, sono certo che prima o poi passerà a Plone 4 (4.1?) ma senza fretta.
Quindi alla lista sopra, aggiungiamo anche il backport del codice a compatibilità con Plone 3.3.

La Collective
La comunità Plone è fantastica, tra le altre cose, per la sua adesione quasi totale all'uso del repository di codice comune: la Collective.
Raramente lo sviluppatore medio si prende il tempo di andare a cercare nella pagina della documentazione di un prodotto se c'è un link al repository del codice. E' certamente buona norma documentarlo esplicitamente (ne ho già parlato) ma i Ponisti sanno dove andare a trovare il codice.
E' come entrare in una casa di un amico, e questo ti chiede "prenditi pure una birra". La cercheresti fuori dal frigo?

Capita mai che il codice non sia dove me lo aspetto? Certo... è capitato, ma credetemi se vi dico che è un evento talmente raro da potermi dimenticare che alle volte succede!

Quale è stata la sorpresa questa volta? Ovviamente il Prodotto X (che tra le altre cose utilizza il namespace collective.xxx) non era sulla collective.

Digressione
Mi immagino già un certo numero di lettori che a queste prime parole stanno già affilando le armi pensando che questa sia una difesa all'uso di SVN. E' come se già leggessi commenti dire "la collective è roba vecchia, ora è meglio passare a Git".
Non lo nascondo, c'è una recente tendenza di spostare la gestione del codice da Subversion a Git e soprattutto Github.
Se persino il core dello sviluppo Plone si sta muovendo verso questa nuova tecnologia, sono certo al 100% che ci siano ottimi motivi ma qui non si sta parlando di quella collective.

Non deve importare la collective come "scelta tecnologia" ma come "idea": un posto dove lo sviluppatore Plone sappia di poter trovare tutto il codice che gli serve. Un posto dove tutti i plonisti del mondo possano collaborare!

Se poi la collective è un reposiroty di codice fatto con SVN, CVS, Git o come copia manuale del codice... non importa! Se c'è uno standard de-facto, usiamolo. Ad oggi avere il codice Plone sulla collective o su Github, non fa grande differenza!

Torniamo al Prodotto X dell'Azienda X
La mia idea, fin dal principio (e credo sia sempre un'ottima idea) è stata naturalmente quella di:
  • tradurre/far tradurre il Prodotto X in italiano
  • aggiungere la funzionalità richiesta dal Cliente X in modo tale che fosse generica (la nuova funzionalità stessa ovviamente andava ben tradotta)
  • far funzionare il prodotto con Plone 3.3 e superiori (attività banale questa volta)
Come si comincia in questi casi? Ecco un semplice consiglio:
Se possibile, non limitiamoci a sistemare in 10 minuti le cose che ci interessano del Prodotto X, magari sul nostro repository di codice privato o tramite patch, ma forniamo agli sviluppatori dell'Azienda X un branch del loro stesso prodotto.

Dopo questo chiediamo all'Azienda X di integrare le nostre modifiche!
Questo consiglio paga? A mio parere sempre. Un branch o una patch applicata ad un prodotto di terze parti che tenete per voi e per il vostro cliente non porterà a nulla.
La sensazione iniziale è di aver velocemente risolto il problema, ma è un'attitudine che guarda molto avanti nel tempo.

Se avessi preso quella strada avrei impiegato meno tempo (non nascondiamolo), ma prima o poi il Prodotto X avrebbe rilasciato una nuova versione (corretto un bug? Aggiornato il codice ad una nuova versione di Plone? Aggiunto una funzionalità?) e io sarei stato costretto ad "inseguire", a riapplicare la patch alla versione successiva del prodotto.
Ecco che quell'ora o due risparmiate oggi diventano quattro ore domani.

La posizione del codice
L'Azienda X esce però dagli standard, commette quindi un piccolo peccato. Il Prodotto X è open source ma il codice non si trovava sulla collective.

Scopro infine che il prodotto è ben documentato e nella documentazione c'è un link al repository del codice: scopro con perplessità che l'Azienda X ha scelto di usare Mercurial e un servizio simile a Github: Bitbucket.

Non so usare Mercurial e non avevo troppo tempo per imparare quei comandi minimi per usarlo tanto da poter creare un branch.
Quello che alla fine ho fatto è stato quindi mandare all'Azienda X un file di patch con le modifiche.
Per arrivare a quel file, quel prodotto finito, ho dovuto fare qualche tentativo in varie direzioni, che alla fine si sono tradotti in... una copia del prodotto nel nostro repository aziendale (questo perché avere il codice sotto controllo di versione permette di tornare sui propri passi con eleganza e velocità).

L'ironia sta tutta qui: la scelta dell'Azienda X di non usare la collective (e la mia incapacità nell'uso di Mercurial ovviamente!) mi ha obbligato ad una serie di operazioni che mi hanno fatto perdere più tempo del dovuto.
Sono convinto che lo sforzo sia stato ben ripagato: la patch è diventata parte del Prodotto X e questo a mio avviso è sempre un buon risultato.

Ecco quindi un nuovo consiglio:
Se il vostro codice è open source ed è nei vostri obbiettivi rilasciarlo, il posto giusto è la collective (+ Github).
L'Azienda Y preferisce un repository interno. Perché?
Sia ben chiaro: lavorare con l'Azienda X è stato molto fruttuoso. La scelta di usare Bitbucket è, come dicevo, un peccato veniale. Il repository era ad ogni modo pubblico e la patch è stata discussa e integrata con estrema velocità.

Esistono motivi per cui un prodotto rilasciato non debba invece rendere disponibile il codice sulla collective?

Non ne trovo. Nemmeno uno!

Non sto dicendo ovviamente che tutto il vostro codice debba essere rilasciato. Sono convinto che una parte di questo (che sia piccola e grande dipende dal vostro tipo di cliente medio) stia bene in posti riservati perché conterrebbe informazioni che riguardano il Vostro Cliente.
La scelta in questi casi è ovviamente il vostro repository, o quello del vostro cliente (nel caso ne abbia uno).

Ma se rilasciate il prodotto, perché non dovreste usare la collective?

Come dicevo, non trovo grandi motivi, ma posso mostrarvi i motivi per cui invece dovreste rilasciare il codice dove tutti possano contribuire.

Fix di bug
Un plonista usa il vostro prodotto e ha trovato un baco. Potrebbe averlo fissato per voi. Una cosa in meno che dovrete correggere voi.
Aggiornamento ad una versione successiva di Plone
Un plonista vuole usare il vostro prodotto e ne crea un branch che lo rende compatibile con una versione di Plone più recente. Quando uno dei vostri clienti, magari gli stessi per cui già avete sviluppato il prodotto, vi chiederanno "funziona con il nuovo Plone", voi potrete dire di sì... e sopratutto dire che qualcun altro ha pagato per quell'aggiornamento. La comunità funziona.
Aggiunta di una funzionalità
Il plonista, di solito tramite un branch, aggiunge al vostro prodotto una nuova funzionalità. Vi pare poco?!
Nuova lingua
Il plonista straniero ama il vostro prodotto e lo traduce nella sua lingua. Il bacino di utenza del vostro prodotto aumenta e con esso la vostra visibilità, o quella della vostra azienda.

Quanto descritto nei casi qui sopra non è qualcosa che ho letto sul "Manuale Etico del Plonista". Sono tutte cose che ho visto succedere... e non una sola volta!

Siate generici, e riciclate
Ecco forse il consiglio più importante.

Il Cliente Y vi chiederà qualcosa di nuovo. La prima cosa da fare è guardarsi intorno. Capire se c'è giù un prodotto che fa al caso vostro. Se lo trovate, Plone (e voi con lui) farete un figurone.

Se questo non accade ma trovate qualcosa di simile, vale quanto detto fin'ora... se la vostra necessità si avvicina al 60/70% o più a quanto fornito da un altro prodotto, valutate se è possibile estendere quel prodotto, o migliorarlo.

Anche in questo caso Plone (e voi) farete una bella figura.

Capitano invece casi in cui dovete praticamente per forza tornare a sviluppare qualcosa di completamente nuovo.

Prima di iniziare a risolvere il problema del Cliente Y a testa bassa pensate se quanto state sviluppando non possa essere reso più generale, e possa quindi essere rilasciato.

Vi è mai capitato, dopo una nuova richiesta, di dirvi "questa richiesta mi è già stata fatta"? Se sì, e non avete reso la soluzione del problema generale, avete perso un'occasione. Il copia/incolla del codice da un prodotto ad un altro non è una soluzione molto mantenibile...

Non inserite un nuovo pezzo di codice "a caso" dentro ad un altro prodotto. Non conta se questa nuova funzionalità vi sembra minima. Spendendo poco in più potreste ottenere qualcosa di prezioso, qualcosa che in futuro potrebbero usare altre persone, ed essere migliorato da altri.

Come già detto: quello che spendete oggi vi ritornerà domani. Investimento!

Prendo un esempio tra i tanti che potrei citare: collective.portaltabs. Questo ricalca perfettamente quando descritto sopra.
Abbiamo incontrato vari utenti che:
  • volevano il controllo dei link in testata del sito
  • avevano paura (o non avevano i poteri) per accedere alla ZMI
Senza un prodotto apposito l'unica soluzione è accedere alla ZMI ed imparare come fare. E' facile? Lo è certamente per molti di voi, ma l'utente inesperto non ama la ZMI. ZMI è dove nulla è in italiano, dove si possono fare "cose pericolose" e dove le cose non sono per nulla chiare.

Quel prodotto non fa nulla di speciale, ma fa qualcosa che vari nostri clienti hanno richiesto.

Traduzioni
Un altro grande ostacolo al rilascio al pubblico sono le traduzioni del prodotto. La tentazione di partire in quarta scrivendo le varie label e descrizioni della vostra interfaccia grafica direttamente in italiano può essere forte.

Molto spesso quello che si ottiene è quindi un buon prodotto, probabilmente anche riutilizzabile e riutilizzato tra vari clienti e installazioni, ma che non può essere rilasciato pubblicamente.

Le prime volte che vi scontrate con il sistema di traduzioni di Plone può sembrare una procedimento molto lento. Ancora una volta: è un investimento. Ad oggi per me è diventato normale scrivere tutto in inglese e tradurre tutto velocemente alla fine.
Fare le traduzioni dei vostri prodotti in Plone è facile, davvero facile (ho scoperto di recente che con la nuova versione 4.1 non ci sono alcuni problemi che ho riscontrato in questi anni) e richiede un tempo aggiuntivo piuttosto misero.

Se fare questo, il vostro prodotto potrà diventare qualcosa che tutta la comunità potrà riutilizzare ed aiutarvi a mantenerlo.

Plonista avvisato...