Molto tardi nel thread, ma una tecnica che ho usato, pre-angolare, è quella di sfruttare JSON e la flessibilità di JS per fare riferimento in modo dinamico alle chiavi di raccolta e utilizzare fatti inalienabili dell'ambiente (nome del server host, lingua corrente del browser , ecc.) come input per discriminare / preferire selettivamente i nomi delle chiavi con suffisso all'interno di una struttura di dati JSON.
Ciò fornisce non solo un contesto di distribuzione (per PO) ma qualsiasi contesto arbitrario (come il linguaggio) per fornire i18n o qualsiasi altra varianza richiesta simultaneamente e (idealmente) all'interno di un singolo manifest di configurazione, senza duplicazioni e chiaramente ovvio.
IN CIRCA 10 LINEE VANIGLIA JS
Esempio eccessivamente semplificato ma classico: un URL di base dell'endpoint API in un file delle proprietà in formato JSON che varia in base all'ambiente in cui (natch) il server host varierà anche:
...
'svcs': {
'VER': '2.3',
'API@localhost': 'http://localhost:9090/',
'API@www.uat.productionwebsite.com': 'https://www.uat.productionwebsite.com:9090/res/',
'API@www.productionwebsite.com': 'https://www.productionwebsite.com:9090/api/res/'
},
...
Una chiave per la funzione di discriminazione è semplicemente il nome host del server nella richiesta.
Questo, naturalmente, può essere combinato con una chiave aggiuntiva basata sulle impostazioni della lingua dell'utente:
...
'app': {
'NAME': 'Ferry Reservations',
'NAME@fr': 'Réservations de ferry',
'NAME@de': 'Fähren Reservierungen'
},
...
L'ambito della discriminazione / preferenza può essere limitato ai singoli tasti (come sopra) in cui il tasto "base" viene sovrascritto solo se esiste un tasto corrispondente + suffisso per gli input della funzione - o un'intera struttura e quella struttura stessa analizzato ricorsivamente per abbinare i suffissi di discriminazione / preferenza:
'help': {
'BLURB': 'This pre-production environment is not supported. Contact Development Team with questions.',
'PHONE': '808-867-5309',
'EMAIL': 'coder.jen@lostnumber.com'
},
'help@www.productionwebsite.com': {
'BLURB': 'Please contact Customer Service Center',
'BLURB@fr': 'S\'il vous plaît communiquer avec notre Centre de service à la clientèle',
'BLURB@de': 'Bitte kontaktieren Sie unseren Kundendienst!!1!',
'PHONE': '1-800-CUS-TOMR',
'EMAIL': 'customer.service@productionwebsite.com'
},
Quindi, se un utente che visita il sito Web di produzione ha un'impostazione delle preferenze della lingua tedesca ( de ), la configurazione di cui sopra crollerebbe in:
'help': {
'BLURB': 'Bitte kontaktieren Sie unseren Kundendienst!!1!',
'PHONE': '1-800-CUS-TOMR',
'EMAIL': 'customer.service@productionwebsite.com'
},
Che aspetto ha una tale magica preferenza / discriminazione come funzione di riscrittura JSON? Non tanto:
// prefer(object,suffix|[suffixes]) by/par/durch storsoc
// prefer({ a: 'apple', a@env: 'banana', b: 'carrot' },'env') -> { a: 'banana', b: 'carrot' }
function prefer(o,sufs) {
for (var key in o) {
if (!o.hasOwnProperty(key)) continue; // skip non-instance props
if(key.split('@')[1]) { // suffixed!
// replace root prop with the suffixed prop if among prefs
if(o[key] && sufs.indexOf(key.split('@')[1]) > -1) o[key.split('@')[0]] = JSON.parse(JSON.stringify(o[key]));
// and nuke the suffixed prop to tidy up
delete o[key];
// continue with root key ...
key = key.split('@')[0];
}
// ... in case it's a collection itself, recurse it!
if(o[key] && typeof o[key] === 'object') prefer(o[key],sufs);
};
};
Nelle nostre implementazioni, che includono siti Web angolari e pre-angolari, avviamo semplicemente la configurazione ben prima di altre chiamate di risorse posizionando il JSON in una chiusura JS autoeseguente, inclusa la funzione prefer (), e alimentando le proprietà di base di nome host e codice lingua (e accetta eventuali suffissi arbitrari aggiuntivi di cui potresti aver bisogno):
(function(prefs){ var props = {
'svcs': {
'VER': '2.3',
'API@localhost': 'http://localhost:9090/',
'API@www.uat.productionwebsite.com': 'https://www.uat.productionwebsite.com:9090/res/',
'API@www.productionwebsite.com': 'https://www.productionwebsite.com:9090/api/res/'
},
...
/* yadda yadda moar JSON und bisque */
function prefer(o,sufs) {
// body of prefer function, broken for e.g.
};
// convert string and comma-separated-string to array .. and process it
prefs = [].concat( ( prefs.split ? prefs.split(',') : prefs ) || []);
prefer(props,prefs);
window.app_props = JSON.parse(JSON.stringify(props));
})([location.hostname, ((window.navigator.userLanguage || window.navigator.language).split('-')[0]) ] );
Un sito pre-angolare ora avrebbe una finestra compressa (no @ suffixed keys) window.app_props a cui fare riferimento.
Un sito angolare, come un passo bootstrap / init, copia semplicemente l'oggetto oggetti di scena scartati in $ rootScope e (facoltativamente) lo distrugge dall'ambito globale / della finestra
app.constant('props',angular.copy(window.app_props || {})).run( function ($rootScope,props) { $rootScope.props = props; delete window.app_props;} );
da iniettare successivamente nei controller:
app.controller('CtrlApp',function($log,props){ ... } );
o indicato da associazioni in viste:
<span>{{ props.help.blurb }} {{ props.help.email }}</span>
Avvertenze? Il carattere @ non è un nome variabile / chiave JS / JSON valido, ma finora accettato. Se questo è un rompicapo, sostituisci qualsiasi convenzione ti piaccia, come "__" (doppio trattino basso) finché ti attieni.
La tecnica può essere applicata sul lato server, trasferita su Java o C # ma l'efficienza / compattezza può variare.
In alternativa, la funzione / convenzione potrebbe far parte del tuo script di compilazione front-end, in modo che il JSON tutto-ambiente / tutto-linguaggio gory completo non sia mai trasmesso via cavo.
AGGIORNARE
Abbiamo evoluto l'uso di questa tecnica per consentire più suffissi a una chiave, per evitare di essere costretti a usare le raccolte (puoi ancora, nel modo che desideri) e anche per onorare l'ordine dei suffissi preferiti.
Esempio (vedi anche jsFiddle funzionante ):
var o = { 'a':'apple', 'a@dev':'apple-dev', 'a@fr':'pomme',
'b':'banana', 'b@fr':'banane', 'b@dev&fr':'banane-dev',
'c':{ 'o':'c-dot-oh', 'o@fr':'c-point-oh' }, 'c@dev': { 'o':'c-dot-oh-dev', 'o@fr':'c-point-oh-dev' } };
/*1*/ prefer(o,'dev'); // { a:'apple-dev', b:'banana', c:{o:'c-dot-oh-dev'} }
/*2*/ prefer(o,'fr'); // { a:'pomme', b:'banane', c:{o:'c-point-oh'} }
/*3*/ prefer(o,'dev,fr'); // { a:'apple-dev', b:'banane-dev', c:{o:'c-point-oh-dev'} }
/*4*/ prefer(o,['fr','dev']); // { a:'pomme', b:'banane-dev', c:{o:'c-point-oh-dev'} }
/*5*/ prefer(o); // { a:'apple', b:'banana', c:{o:'c-dot-oh'} }
1/2 (utilizzo di base) preferisce i tasti '@dev', scarta tutti gli altri tasti con suffisso
3 preferisce '@dev' rispetto a '@fr', preferisce '@ dev & fr' rispetto a tutti gli altri
4 (uguale a 3 ma preferisce '@fr' rispetto a '@dev')
5 nessun suffisso preferito, elimina TUTTE le proprietà suffissate
Ciò si ottiene assegnando un punteggio a ciascuna proprietà con suffisso e promuovendo il valore di una proprietà con suffisso sulla proprietà senza suffisso durante l'iterazione delle proprietà e la ricerca di un suffisso con punteggio più elevato.
Alcune efficienze in questa versione, inclusa la rimozione della dipendenza da JSON per il deep-copy e il ricorrere solo in oggetti che sopravvivono al round di punteggio in profondità:
function prefer(obj,suf) {
function pr(o,s) {
for (var p in o) {
if (!o.hasOwnProperty(p) || !p.split('@')[1] || p.split('@@')[1] ) continue; // ignore: proto-prop OR not-suffixed OR temp prop score
var b = p.split('@')[0]; // base prop name
if(!!!o['@@'+b]) o['@@'+b] = 0; // +score placeholder
var ps = p.split('@')[1].split('&'); // array of property suffixes
var sc = 0; var v = 0; // reset (running)score and value
while(ps.length) {
// suffix value: index(of found suffix in prefs)^10
v = Math.floor(Math.pow(10,s.indexOf(ps.pop())));
if(!v) { sc = 0; break; } // found suf NOT in prefs, zero score (delete later)
sc += v;
}
if(sc > o['@@'+b]) { o['@@'+b] = sc; o[b] = o[p]; } // hi-score! promote to base prop
delete o[p];
}
for (var p in o) if(p.split('@@')[1]) delete o[p]; // remove scores
for (var p in o) if(typeof o[p] === 'object') pr(o[p],s); // recurse surviving objs
}
if( typeof obj !== 'object' ) return; // validate
suf = ( (suf || suf === 0 ) && ( suf.length || suf === parseFloat(suf) ) ? suf.toString().split(',') : []); // array|string|number|comma-separated-string -> array-of-strings
pr(obj,suf.reverse());
}
'ngconstant:development'
in'serve'
- se lo metti in config di orologio sotto'gruntfile'
cometasks: ['ngconstant:development']
- non sarà necessario riavviaregrunt serve
quando si aggiornano le variabili di sviluppo nel gruntfile.