Newsgroup: it.comp.dotnet
Titolo: A che livello mettere la localizzazione?

Ciao a tutti,
con dei colleghi stiamo lavorando ad una applicazione web piuttosto 
complessa che dovrà essere installata (già da subito) in tre nazioni 
differenti, con tre lingue differenti. :-)
Ogni metodo del service layer ritorna una oggetto di tipo:
class MethodResult<T> {
	T Result { get; }
	bool Success { get; }
	string[] ErrorMessages { get; }
}
stiamo discutendo "alacremente" :-) sulla questione: in ErrorMessages ci 
vanno stringhe già tradotte/localizzate o è meglio inserire un codice di 
errore e delegare l'interpretazione al presentation layer? Esiste 
qualche guideline ufficiale?
Grazie,
Giulio
-- 


Io propendo per la delega ai layer più alti. Ma più che di un "codice" 
preferirei parlare della "formattazione". Cioè passare un qualcosa che 
contiene l'id del messaggio ma anche eventuali parametri per la 
formattazione. Qualcosa che ti permetta di scrivere stringhe tipo:
"Attenzione, il semaforo 25 è rosso" dove il numero 25 e la stringa 
"rosso" sono parametri passati alla "format" finale. E dove il parametro 
"rosso" potrebbe non essere direttamente una stringa ma a sua volta un 
ID da pescare dalle risorse localizzate.
Chiederai: perché tutta questa complicazione? Per poterti permettere di 
scrivere ad esempio due log: il primo nella lingua dell'applicazione, il 
secondo nella lingua "neutra" dello sviluppatore.
Io ho dovuto fare così perché altrimenti quando mi mandavano dei log in 
russo non ci capivo nulla.
Non so se esista in giro qualcosa di precotto, io in c++ nativo mi sono 
dovuto arrangiare...
HTH

 russo non ci capivo nulla.
Quanto ri capisco... :-)
Ciao,
Giulio
-- 

 errore e delegare l'interpretazione al presentation layer?
Ti dico la mia: delegherei al presentation layer in modo da passare in giro 
un codice piuttosto che una stringa; per semplicita', tornerei direttamente 
il resourcekey/ResourceID che punta al messaggio di errore nelle risorse 
localizzate.
-LV
 
--- Posted via news://
freenews.netfront.net/
 - Complaints to 
ne...@netfront.net
 ---

 piuttosto che una stringa
Pensandoci meglio, mi sembra che il presentation layer sia in ogni caso il 
luogo deputato (possibilmente nel markup).


 nelle risorse localizzate.
Ok, grazie della dritta, credo di aver capito l'antifona. :-)
Tuttavia volevo "armonizzare" questo suggerimento con quello di GM.
Supponiamo che sia nel caso di dover avere un messaggio "parametrico", 
quindi nel file di risorse avrò un format per un messaggio di errore. In 
generale al PL dovranno essere passati:
1. un id per il format dell'errore
2. una lista di parametri che possono essere:
	2.1 valori veri e propri
	2.2 id di valori da prendere dalle risorse
C'è qualcuno che si è trovato in questa situazione? Qualche spunto?
Grazie,
Giulio
-- 

 il corrispondente errore?
+1
.m
-- 
Mauro Servienti
{C67C0157-5D98-4733-A75E-93CAEE4BADC8}
Microsoft MVP - Visual C# / MCTS
http://mvp.support.microsoft.com/profile/mauro
blog @ 
http://milestone.topics.it
whynot [ at ] topics [ dot ] it

 Giulio


 localizzarlo in un'altra lingua se è uno scenario sensato).
Interessante. :-)
Provo a sviluppare un altro po' la discussione. Confesso di fare fatica 
a capire come indicare in maniera chiara e sopratutto senza duplicare 
logica sparpagliata un po' qua e un po' là tutte queste cose qui.
L'esempio postato da GM è molto didattico: "Il semaforo numero 25 è 
rosso". Ora, mentre 25 è sempre quello :-) la parola "rosso" può 
diventare "red", "rouge", "rojo", "vermelho". Quindi in portoghese il 
mio messaggio dovrebbe essere generato dal format: "O semàforo {0} està 
vermelho {1}" richiamato con 25 e "vernelho". Il mio oggetto ritornato 
dovrebbe contenere:
1. Codice di errore
2. chiave del format
3. lista di parametry che possono essere stringhe da inserire 
direttamente o chiavi di altre risorse
Potete linkarmi a qualche esempio di codice che realizzi qualcosa del 
genere?
Grazie,
Giulio
-- 

Essendo io uno dei "colleghi" mi intrometto nella discussione e
rispondo al perché su quella struttura. Quella struttura ha il fine di
uniformare le risposte dei servizi in modo da avere disponibile, oltre
al risultato dell'operazione, anche la motivazione del risultato. In
questo modo abbiamo la possibilità di gestire allo stesso modo errori
dovuti ad eventuali eccezioni, o errori dovuti a logiche applicative
che non permettono l'esecuzione dell'operazione richiesta...
La mia proposta era questa
    public class ActionResult<T>
    {
        public ResultCodes ResultCode;
        public T Result;
        public string Description;
    }
che ha, oltre al codice di errore definito dall'enum ResultCodes,
anche un'eventuale descrizione dell'errore.


Come ti dicevo, io feci la cosa in C++ nativo, quindi qui è un po diverso...
in dot net tutto dovrebbe essere semplificato dalla IFormattable, 
CultureInfo e ResourceManager.
Se non sbaglio la String.Format è in grado di accettare un array di 
parametri che se sono di tipo IFormattable vengono gestiti in un certo 
modo. Io proverei a vedere se è possibile agganciarsi qui e 
eventualmente implementare un tipo ResString che implementa 
IFormattable, incapsula un ID e va a pescare la stringa dalle risorse 
giuste. Non mi stupirei se questa cosa esistesse già...

 2.2 id di valori da prendere dalle risorse
Piuttosto che tornare un oggetto "Result", solleverei una eccezione in caso 
di "errore". Nello scenario che andate delineando, probabilmente userei due 
tipi eccezioni derivate dalla stessa classe base, per distinguere fra 
eccezioni di sistema e violazioni di business rules. Tali eccezioni custom 
dovrebbero incapsulare, oltre ai dettagli della eventuale eccezione 
sottostante (e quindi della eventuale cascata di inner exceptions), tutte le 
info aggiuntive necessarie, ad esempio, alla localizzazione.
Il logging sono d'accordo che non e' un problema: si logga in lingua neutra, 
quindi in Inglese, e si loggano le info dettagliate della eccezione (e delle 
eventuali inner exceptions) e del contesto in cui si e' verificata. Al 
presentation layer ripeto che farei arrivare solo "codici": e' compito del 
PL tradurre tali indicazioni in messaggi (localizzati) per l'utente.
Su questo ultimo punto, cioe' su cosa effettivamente tornare al PL, a mio 
avviso non esiste una regola fissa, occorre considerare i requisiti e il 
solito ammonimento a non sovra-ingegnerizzare. Comunque, mi piace l'idea che 
le stringhe localizzate siano piuttosto dei format: eviterei pero' cose come 
da tuo punto 2.2, cioe' mi limiterei alla logica base format piu' array di 
oggetti valore, in modo da avere bassa complessita' anche al costo di 
qualche duplicazione nei testi da dover inserire (anche l'archivio risorse 
va gestito e non deve essere necessario essere dei super-esperti per farlo).
Resterebbe da decidere se nelle exception vanno direttamente l'id della 
risorsa e l'array di parametri, oppure se vale la pena fare un qualcosa di 
leggermente piu' strutturato/disaccoppiato e avere, per esempio, una 
gerarchia di eccezioni piu' fine (contro codici di errore) in cui sono 
definite proprieta' rilevanti alla eccezione specifica (contro l'array di 
parametri), e poi avere un po' di logica al livello di PL che mappa 
l'eccezione e le sue proprieta' appunto e di nuovo all'id del format e 
all'array di parametri.
Comunque stiamo spaccando il capello: nel quotidiano non approccio lo 
sviluppo cosi' prevalentemente a tavolino. Quando devo implementare una 
funzionalita', tendo a partire  da soluzioni elementari e non 
particolarmente stutturate, che poi sottopongo a cicli di ristrutturazione e 
raffinamento dettati piu' che altro dall'evolversi del sistema. In 
particolare, e' solo analisi che mi piace fare il piu' possibile up-front, 
mentre design e codice li affronto in modo il piu' possibile incrementale...


Ciao emidio,

ok, tutto chiaro, gaurda la risposta che ho dato a Giulio che include 
anche un ragionamento su questo.


 Potete linkarmi a qualche esempio di codice che realizzi qualcosa del genere?
secondo me non hai bisogno di sparpagliare logica, parti sempre 
dall'error code (un valore numerico o un'enumerazione) passi 
quell'error code a qualcuno tipo un
IFaultProvider.Format( Int32 errorCode, CultureInfo culture, 
Func<String, String> formatterCallback  );
quindi puoi scrivere una cosa del tipo:
var msg = formatter.Format( errorCode, new CultureInfo( "en-US" ), s=> 
String.Format( s, v1, v2 ) );
dove v1 e v2 sono valori "locali" a dove si è verificato il problema, 
naturalmente devi sapere up-front che la stringa localizzata è 
"formattabile".


 all'array di parametri.
La storia delle eccezioni specializzate mi "spaventa" un po'. Non si 
rischia di cadere nel caso della sovraingegnerizzazione di ui parlavi prima?
Ciao,
Giulio
-- 

 "formattabile".
Vediamo se ho capito:
1. inietto IFaultProvider nel presentation layer
2. nell'eccezione sollevata/oggett di ritorno ci metto il codice di 
errore e la stringa lambda expression per la formattazione (la 
cultura... non lo so, ha senso che il SL sappia in che lingua stiamo 
lavorando?)
Corretto?
Grazie,
Giulio
-- 

 Corretto?
si, direi che il concetto di fondo è giusto.


 rischia di cadere nel caso della sovraingegnerizzazione di ui parlavi prima?
Usare eccezioni in questo caso e' semplicemente the way to go in .Net, con una serie di ricadute architetturali cui altri hanno gia' accennato: per non dire che, ad occhio, e' quasi sicuro che di codice ne scrivete semmai di meno.
Il rischio di sovra-ingegnerizzazione stava altrove: << eviterei pero' cose come da tuo punto 2.2 >> e le altre considerazioni sul design minuzioso up-front. E' sufficiente una decente padronanza della tecnologia in uso con i suoi idiomi di base per non uscire dai binari. Evito ulteriori digressioni sui vantaggi di un approccio incrementale nelle fasi cosiddette tecniche (design, coding, testing).
-LV

 security perchè riveli verso l'alto dettagli interni.
Sei esposto a rischi *se* riveli verso l'esterno dettagli interni: non fa una piega che _le eccezioni non devono uscire dai boundaries_, ma cio' non comporta nessun problema intrinseco alle inner exception e che queste vadano ignorate/droppate.
-LV

 -LV


 ragionamenti/filtri sulla base della culture.
Giusto.
Ciao e grazie,
Giulio
--

 }
Ok, riprendendo l'esempio fatto da GM: "Il semaforo 25 è rosso" che in 
portoghese mi diventa "O semáforo número 25 está vermelho"; 25 è 
universale mentre il colore dovrebbe essere passato come chiave di una 
particolare risorsa. Ovviamente con un object semplice non me la cavo... :-)
Comunque a parte questo, mi sembra tutto chiaro.
Grazie per i suggerimenti,
Giulio
-- 

Ciao Mauro,
On 5/16/2011 9:31 AM, Mauro Servienti [MVP] wrote:
  [cut]
Alla luce di quanto è seguito nel thread, mi permetto di aggiungere una 
domanda. E se il servizio in questione fosse (ad esempio) un servizio WCF?
Ciao,
Giulio
-- 

 Ok.
Come gia' detto, non e' OK: quella affermazione semplicemente non e' corretta.
-LV

 ovvio, è quello che ci scrivi il problema non le InnerException in sè.
Menzionare le inner exception in questo caso e' proprio fuorviante: sono le *exception*, senza ulteriori qualificazioni, che non devono attraversare i bounday inter-componente (e il tuo codice per OperationResult<T>
andrebbe spiegato meglio che e' da intendersi intra-bounday).
-LV

 Comunque a parte questo, mi sembra tutto chiaro.
Benissimo.
-LV

 Come gia' detto, non e' OK: quella affermazione semplicemente non e' corretta.
Cambiando discorso: sono solo io che continuo a vedere le tue risposte 
non ordinate nel thread? Gli altri?
Ciao,
Giulio
-- 

 :-)
Non ne sarei sicurissimo... Te la butto li:
class ResString : IFormattable
{
     public ResString(string stringID, System.Resources.ResourceManager 
rm, params Object[] args)
     {
         this.StringId = stringID;
         this.rm = rm;
         this.args = args;
     }
     public string ToString(string format, IFormatProvider formatProvider)
     {
         if(format == null)
             format = rm.GetString(StringId, formatProvider as 
System.Globalization.CultureInfo);
         return string.Format(formatProvider, format, args);
     }
     private string StringId;
     private System.Resources.ResourceManager rm;
     private Object[] args;
}
class Program
{
     static void Main(string[] args)
     {
         ResString col = new ResString("colorRed", 
Resource1.ResourceManager);
         ResString Msg = new ResString("msg", Resource1.ResourceManager, 
25, col);
         System.Globalization.CultureInfo ci = new 
System.Globalization.CultureInfo("en");
         Console.WriteLine(Msg);
         Console.WriteLine(Msg.ToString(null, ci));
         Console.WriteLine(Msg.ToString("sem:{0} color:{1}", null));
         Console.WriteLine(Msg.ToString("sem:{0} color:{1}", ci));
     }
}

 Usare eccezioni in questo caso e' semplicemente the way to go
La domanda iniziale è: come fa il service layer a notificare al 
presentation layer che qualcosa è andato storto? Se possiamo considerare 
la comunicazione SL-PL come "intra-boundary", allora non dovrebbero 
viaggiare eccezioni.
Diciamo che sono confuso, via. :-)


 Non ne sarei sicurissimo... Te la butto li:
[cut]
Grazie mille dell'esempio: un po' di codice scritto è sempre 
esplicativo! :-)
Rettifico però il problema: più che sul *cosa* fare, mi sento in 
difficoltà sul *come* farlo. Andando al codice:
interface IFooService {
	XXX DoSomethingWithMyFoo(String fooId);
}
questo servizio è consumato da un Controller. Con riferimento 
all'esempio da te postato,
1. cosa dovrei metterci in XXX?
2. chi si occupa di generare le ResString, con conoscenza della cultura 
corrente, ecc ecc...?
ciao e grazie mille,
Giulio
-- 
 


ResString è il tuo messaggio. Incapsula l'id della stringa e l'elenco 
degli argomenti di formattazione. Alcuni di questi argomenti potrebbero 
essere a loro volta dei ResString il che ti permette di creare 
bellissimi giochini ricorsivi tipo:
"Il messaggio delle ore {0} era: {1}"
che costruisci passando due argomenti: un DateTime e un ResString che 
contiene la celeberrima :"Il semaforo numero {0} è {1}" che a sua volta 
costruisci passando 25 e un ulteriore ResString che incapsula l'id della 
stringa "rosso"
ResString col = new ResString("colorRed", Resource1.ResourceManager);
ResString Msg2 = new ResString("msg2", Resource1.ResourceManager, 25, col);
ResString Msg = new ResString("msg1", Resource1.ResourceManager, 
System.DateTime.Now, Msg2);
Supponiamo che nelle risorse ci siano le stringhe:
colorRed	rosso	
msg1	il messaggio delle ore {0:t} era "{1}"
msg2	il semaforo {0} è {1}	
e in inglese:
colorRed	red	
msg1	"{1}" was the {0:t} message	
msg2	the semaphore number {0} is {1}	
Chi riceve un ResString può formattarlo e eventualmente passare una 
cultura con cui vorrebbe vedere il messaggio. Sfruttando il meccanismo 
di fallback del resourcemanager il messaggio viene formattato e la 
stessa ResString iniziale diventa:
il messaggio delle ore 17.43 era "il semaforo 25 è rosso"
oppure supponendo di passare alla ToString la cultura en
"the semaphore number 25 is red" was the 5:43 PM message
HTH

 Diciamo che sono confuso, via. :-)
Parte della confusione forse nasce dal fatto che, ad esempio, il boundary SL-PL puo' essere sia inter- che intra-: dipende dai casi e dalle specifiche esigenze di sicurezza.
Un'altra possibile fonte di confusione e' che, anche nel caso la comunicazione in questione e' inter-componente (ovvero anche nel caso ci sia necessita' di evitare propagazione di dettagli), si possono comunque propagare eccezioni. L'accorgimento sta nel non propagare le eccezioni originali, cioe' quelle interne e dettagliate, bensi' sul boundary intercettarle e rilanciare eccezioni specifiche per l'esterno, con i dati strettamente necessari.
Nota che ho detto "si possono", non "si devono": di varianti sulle implementazioni specifiche ce ne sono piu' o meno a volonta.
HTH,
-LV
P.S. Ovviamente sto postando da Google Groups...

 bellissimi giochini ricorsivi tipo: <snip>
L'idea e' "bella" e persino semplice da realizzare: glielo spieghi poi tu pero' al copy-writer di turno, il quale probabilmente partira' dai testi in Inglese per tradurre in un'altra lingua, come funziona quel "bellissimo giochino ricorsivo"... In realta', non dico che sia necessariamente da evitare, pero' desideravo segnalare che certi impatti non vanno sottovalutati: se fossi responsabile del progetto e il progetto stesso fosse non banale (cioe' con decine e decine di messaggi diversi), semplicemente non avallerei una scelta del genere.
Anyway, my 2c.
-LV

 implementazioni specifiche ce ne sono piu' o meno a volonta.
Sai linkarmi a qualche documento/guideline/bestpractice?
Grazie e buona giornata,
Giulio
-- 

 cultura con cui vorrebbe vedere il messaggio
Ecco, il punto sta proprio sul *chi*. :-)
Se al PL arrivano le ResString, significa che la conoscenza delle 
risorse sta sia nel PL che nel Service Layer. Come approccio non è che 
mi faccia impazzire perchè significa duplicare le cose. Oppure ho 
proprio sbagliato a capire?


 Sai linkarmi a qualche documento/guideline/bestpractice?
La tua discussione che abbiamo avuto e' piuttosto ampia quindi non saprei...
Prova a guardare questi due link per alcune guidelines:
http://msdn.microsoft.com/en-us/library/5b2yeyab.aspx
http://msdn.microsoft.com/en-us/library/ms229014.aspx
Potresti anche a fare qualche ricerca sul Web.
