Dag allen 
Ik ben een beetje aan het frutselen met een site die tweetalig is. Ik wil het wel mooi/elegant doen dus niet overal en/nl hardcoden maar dus indirect de optie open houden om meer talen toe te voegen. Vooral zodat ik delen code eventueel later kan hergebruiken, of iig de ideeën erachter.
Nu heb ik wat zitten lezen en kwamen er een paar veelgebruikte DB-modellen naar voren:
1) Elke localizable string in een aparte tabel zetten met bijbehorende Locale en een common key. Die key sla je dan in de originele tabel op. Plus: Heel flexibele oplossing. Min: Heel veel queries en lastig te gebruiken in bijv een ORM
2) Kolommen voor elke vertaalde versie (dus name_nl, name_en). Plus: Enigszins makkelijk? Min: Totaal niet schaalbaar en imo helemaal niet netjes
3) Losse tabellen voor elke vertaalde versie (dus posts_nl, posts_en). Plus: Makkelijker? Als je je ORM wat modifiet kan het wellicht wel. Min: Niet heel storage-vriendelijk. Concurrency problemen. Lastig te onderhouden.
Nummer 1 leek mij dus het handigste in dit geval, dus ik ben een beetje gaan rondlezen hoe je dit goed met Entity Framework (ORM in dit geval) kan gebruiken. Nu kwam ik een (imo) erg elegante oplossing tegen die gebruik maakt van RealProxy's (in C#/.NET, http://stackoverflow.com/a/17002970). Het idee is dat die proxy de getters voor de resources onderschept, en de huidige waarde (de key) daarna opzoekt in de DB en de goede translated versie teruggeeft. Met enige caching voor de performance natuurlijk. Met ProxyAttribute kan je deze proxy automagisch bij new teruggeven waardoor je in het gebruik niet merkt dat er op de achtergrond dingen veranderen. Ik vind dit erg elegant, maar kan me heel goed voorstellen dat mensen dit te veel magie vinden. Wat dit bijvoorbeeld mogelijk maakt:
In dit geval is foo.Name dan vanzelf de vertaalde versie voor de current locale.
Maar ik merkte hier een paar problemen mee:
- De updatetracker van EF doet ook een get en ziet een andere waarde en gaat dan de DB updaten waarna de key overeenkomt met de vertaalde versie
- Het updaten van vertaalde versies is nog steeds lastig
Hiervoor ben ik gaan prutsen met meerdere oplossingen/ideeën maar ik vond geen van allen echt mooi/elegant/goed werkend:
1) Voor 1: in de proxy calls vanuit EntityFramework assembly detecteren en aan die calls de originele key teruggeven bij een get. Voor 2: een normale set (niet vanuit EF) wordt ook onderschept en slaat de vertaalde versie op. De setter vanuit EF set de key in het veld.
Nadeel: Heel erg nasty om assemblies te detecteren. Niet flexibel met andere ORMs.
Voordeel: eenvoudig gebruik voor get/set, werkt allemaal vanzelf goed
2) Omdat de voordelen van 1 toch wel aantrekkelijk waren heb ik gekeken naar een andere manier om calls van EF te 'redirecten'. Ik heb toen iets bedankt om EF 'voor de gek te houden', het is niet heel elegant maar werkt wel. At runtime wordt de entity dynamisch geëxtend in een nieuwe class die xxxKey properties bevat voor alle te vertalen waarden (love TypeBuilder). In het normale gebruik wordt deze class gewoon als naar de BaseType gecast, maar EF herkent door het gebruik van reflection die nieuwe properties wél en gebruikt die dan tijdens het getten/setten. De proxy gebruikt de waarden uit die xxKey properties om de translated strings op te halen/op te slaan. De nieuwe class (FooLocalized) heeft dan dus Name en NameKey properties. De eerste wordt geïgnored in EF.
Dus voordelen: makkelijk in gebruik, weinig nadenken tijdens gebruik, databasemodel is direct goed/duidelijk zonder veel werk én ik heb het al werkend en het werkt wel echt goed (de taalafhandeling is compleet op de achtergrond, dus dat laat je focussen op de rest van de logica/het programma).
Nadeel: Het is nóg meer magie én de Fluent API voor EF is onbruikbaar geworden. Je kan eigenschappen van properties (stringlength, blabla) enkel met attributes setten, waardoor de flexibiliteit van EF een klein beetje verloren gaat (aangepaste many-to-many tabelnamen anyone? Meerdere relations naar dezelfde entity?). Dit omdat de Fluent API grotendeels met generics werkt en het type van de extended class enkel beschikbaar is on runtime. Ik heb geprobeerd alle eigenschappen geset op de originele class over te zetten naar de nieuwe class, maar dan moet je met reflection private readonly fields gaan aanpassen
3) Het hele idee van een proxy verlaten en gewoon functies in elke class maken die de localized versie ophalen. (GetLocalizedName oid)
Voordeel: Weinig magie
Nadeel: Heel veel handwerk bij elke class, veel herhalende code, veel logica in je models, heleboel werk weggooien.
Wat ik het mooiste zou vinden is om, zoals bij 2), gewoon enkel een attribuut te hoeven setten bij een property en dat dan de rest vanzelf gebeurt. Het is eenvoudig uitbreidbaar en zou eventueel ook als library in mijn andere projecten gebruikt kunnen worden. Maar ik kan nou niet echt een oplossing vinden voor de problemen. Wat vinden jullie? Is het überhaupt aan te raden om voor de proxies te gaan? Ben ik heel dom bezig? (vast) Of hebben jullie een ander idee om de problemen met de proxy op te lossen, of een alternatief voor de proxy?

Ik ben een beetje aan het frutselen met een site die tweetalig is. Ik wil het wel mooi/elegant doen dus niet overal en/nl hardcoden maar dus indirect de optie open houden om meer talen toe te voegen. Vooral zodat ik delen code eventueel later kan hergebruiken, of iig de ideeën erachter.
Nu heb ik wat zitten lezen en kwamen er een paar veelgebruikte DB-modellen naar voren:
1) Elke localizable string in een aparte tabel zetten met bijbehorende Locale en een common key. Die key sla je dan in de originele tabel op. Plus: Heel flexibele oplossing. Min: Heel veel queries en lastig te gebruiken in bijv een ORM
2) Kolommen voor elke vertaalde versie (dus name_nl, name_en). Plus: Enigszins makkelijk? Min: Totaal niet schaalbaar en imo helemaal niet netjes
3) Losse tabellen voor elke vertaalde versie (dus posts_nl, posts_en). Plus: Makkelijker? Als je je ORM wat modifiet kan het wellicht wel. Min: Niet heel storage-vriendelijk. Concurrency problemen. Lastig te onderhouden.
Nummer 1 leek mij dus het handigste in dit geval, dus ik ben een beetje gaan rondlezen hoe je dit goed met Entity Framework (ORM in dit geval) kan gebruiken. Nu kwam ik een (imo) erg elegante oplossing tegen die gebruik maakt van RealProxy's (in C#/.NET, http://stackoverflow.com/a/17002970). Het idee is dat die proxy de getters voor de resources onderschept, en de huidige waarde (de key) daarna opzoekt in de DB en de goede translated versie teruggeeft. Met enige caching voor de performance natuurlijk. Met ProxyAttribute kan je deze proxy automagisch bij new teruggeven waardoor je in het gebruik niet merkt dat er op de achtergrond dingen veranderen. Ik vind dit erg elegant, maar kan me heel goed voorstellen dat mensen dit te veel magie vinden. Wat dit bijvoorbeeld mogelijk maakt:
C#:
1
2
| var foo = Db.Foos.First(); DoSomethingWith(foo.Name); |
In dit geval is foo.Name dan vanzelf de vertaalde versie voor de current locale.
Maar ik merkte hier een paar problemen mee:
- De updatetracker van EF doet ook een get en ziet een andere waarde en gaat dan de DB updaten waarna de key overeenkomt met de vertaalde versie
- Het updaten van vertaalde versies is nog steeds lastig
Hiervoor ben ik gaan prutsen met meerdere oplossingen/ideeën maar ik vond geen van allen echt mooi/elegant/goed werkend:
1) Voor 1: in de proxy calls vanuit EntityFramework assembly detecteren en aan die calls de originele key teruggeven bij een get. Voor 2: een normale set (niet vanuit EF) wordt ook onderschept en slaat de vertaalde versie op. De setter vanuit EF set de key in het veld.
Nadeel: Heel erg nasty om assemblies te detecteren. Niet flexibel met andere ORMs.
Voordeel: eenvoudig gebruik voor get/set, werkt allemaal vanzelf goed
2) Omdat de voordelen van 1 toch wel aantrekkelijk waren heb ik gekeken naar een andere manier om calls van EF te 'redirecten'. Ik heb toen iets bedankt om EF 'voor de gek te houden', het is niet heel elegant maar werkt wel. At runtime wordt de entity dynamisch geëxtend in een nieuwe class die xxxKey properties bevat voor alle te vertalen waarden (love TypeBuilder). In het normale gebruik wordt deze class gewoon als naar de BaseType gecast, maar EF herkent door het gebruik van reflection die nieuwe properties wél en gebruikt die dan tijdens het getten/setten. De proxy gebruikt de waarden uit die xxKey properties om de translated strings op te halen/op te slaan. De nieuwe class (FooLocalized) heeft dan dus Name en NameKey properties. De eerste wordt geïgnored in EF.
Dus voordelen: makkelijk in gebruik, weinig nadenken tijdens gebruik, databasemodel is direct goed/duidelijk zonder veel werk én ik heb het al werkend en het werkt wel echt goed (de taalafhandeling is compleet op de achtergrond, dus dat laat je focussen op de rest van de logica/het programma).
Nadeel: Het is nóg meer magie én de Fluent API voor EF is onbruikbaar geworden. Je kan eigenschappen van properties (stringlength, blabla) enkel met attributes setten, waardoor de flexibiliteit van EF een klein beetje verloren gaat (aangepaste many-to-many tabelnamen anyone? Meerdere relations naar dezelfde entity?). Dit omdat de Fluent API grotendeels met generics werkt en het type van de extended class enkel beschikbaar is on runtime. Ik heb geprobeerd alle eigenschappen geset op de originele class over te zetten naar de nieuwe class, maar dan moet je met reflection private readonly fields gaan aanpassen

3) Het hele idee van een proxy verlaten en gewoon functies in elke class maken die de localized versie ophalen. (GetLocalizedName oid)
Voordeel: Weinig magie
Nadeel: Heel veel handwerk bij elke class, veel herhalende code, veel logica in je models, heleboel werk weggooien.
Wat ik het mooiste zou vinden is om, zoals bij 2), gewoon enkel een attribuut te hoeven setten bij een property en dat dan de rest vanzelf gebeurt. Het is eenvoudig uitbreidbaar en zou eventueel ook als library in mijn andere projecten gebruikt kunnen worden. Maar ik kan nou niet echt een oplossing vinden voor de problemen. Wat vinden jullie? Is het überhaupt aan te raden om voor de proxies te gaan? Ben ik heel dom bezig? (vast) Of hebben jullie een ander idee om de problemen met de proxy op te lossen, of een alternatief voor de proxy?
2x Dell UP2716D | R9 7950X | 128GB RAM | 980 Pro 2TB x2 | RTX2070 Super
.oisyn: Windows is net zo slecht in commandline als Linux in GUI