How can I implement multi-tenant translations in Symfony2 - symfony

I am developing a multi-tenant capable Symfony2 solution and was wondering if there was a way to use different translations files for each tenant, as the default translations files at present contain e.g. references to the initial tenant's company name, etc.
I am using the Liip Theme Bundle (https://github.com/liip/LiipThemeBundle) to allow tenants to use our codebase, layering their own design on top, but cannot work out a simple and scalable way to allow them to use their own translations files.
There was talk on the theme bundle git repo about this, but I don't believe anything was ever implemented (https://github.com/liip/LiipThemeBundle/issues/12). Ideally I'd like to follow the directory structure they suggested in that thread, e.g.
root
- app
- Resources
- themes
- <theme name>
- public
- translations (this would be new)
- views
as this would allow us to continue the practice of themes being self-contained git submodules that a tenant can maintain themselves.

I ended up using the directory structure I outlined above, and had a console command which symlinked the translations override file in app/Resources/translations. This command ran during my deploy script, and I then created my own "trans" twig function which checked if an override file should be used.
Not the cleanest, but definitely works the way I wanted.

Related

Symfony 4 and Microservices

Say I'm going to create few microservices: Alpha, Beta, Gamma.
In terms of Application structure using older Symfony version like 2, I'd create a bundle for each service, but bundles are no longer recommended in Symfony 4. So... Should I create separate repositories for every service or still create a bundles in a one App?
If you have different microservices, as in different applications, you will not need bundles. You can keep them in different repositories, but a common practice is to use a so called mono-repository. As the name suggests, with a mono-repository you keep all of the projects in a single repository. This has the benefit that changes spanning all projects can be done more easily and in sync. The drawback is that it requires more effort when managing and might cause additional overhead when building and deploying as it will not be easy to see which service has changed so must likely you rebuild all of them. There are a few books and presentations on mono-repositories you might want to check out. In short, Symfony does not restrict how you manage your services. You can have a single repository for all projects or multiple repositories.
If you want to serve all "services" through the same application, even without bundles, you can do so by using namespaces to separate the logic, e.g. for controllers:
my_app
- src
- Controller
- Alpha
- IndexController
- Beta
- IndexController
This should work out of the Box with the default configuration and even if you deviate you can make things like argument resolvers work by just pointing the configuration to the correct folder. Obviously this will require you to make sure that code is not shared between services should you ever want to extract them into their own application. There are some static code analyis tools that help you with keeping your architecture clean, i.e. make sure Alpha does not use code from Gamma and vice versa.
If you want to separate the apps more clearly by doing something like this:
my_app
- src
- AlphaApp
- ...
- BetaApp
- ...
You can still do that but it will require more manual work and the recipes will not work anymore, requiring you to do manual changes to most configurations and moving around files. How to do it depends on whether you want a shared kernel or a separate kernel for each service, but if you go that route I recommend keeping separate projects in the same repository, as it will probably yield cleaner results and be less work.
You can still create bundles in symfony4 though its not recommended by best practices. see https://symfony.com/doc/current/best_practices/creating-the-project.html

Where to save your TWIG templates in Symfony framework

What is the best place to keep your TWIG template and why?
app/Resources/views folder
or
YourBundle/Resources/views
Traditionally, Symfony developers stored the application templates in
the Resources/views/ directory of each bundle. Then they used the
logical name to refer to them (e.g.
AcmeDemoBundle:Default:index.html.twig).
But for the templates used in your application, it's much more
convenient to store them in the app/Resources/views/ directory.
Syfmony Documentation
In Symfony 2, auto generated twig files were located in YourBundle/Resources/views
In Symfony 3, auto generated twig files are located in app/Resources/views
When developing your application, best place is YourBundle/Resources/views. It will be more consistent and probably it will be easier to maintain and eventually, decouple your bundle for installation in other projects.
I find app/Resources/views useful to override other bundle's templates, for example when creating Error pages http://symfony.com/doc/current/cookbook/controller/error_pages.html
It depends of your bundles architecture. The point is to be consistent with your current architecture.
From the official doc:
For most projects, you should store everything inside the AppBundle
If you use a main bundle structure (like a big AppBundle with many small domain bundles) then you probably want to centralize your templates in App or the ressources of your main Bundle.
But if you use a functionnal bundles (like EmailBundle, UserBundle, Invoice Bundle) then you probably want to ignore the symfony best practice and put the concerned twig in these bundles.

symfony2: how to manage your translations

In my symfony2 application, i have set up what's necessary to change the locale.
Now I can use the trans features of the bundle and generate the language files under app/resources/translations
That works fine but I feel it's not efficient to edit yml files by hand, and it's advised to use xliff which is absolutely not user friendly.
Plus, in a collaborative environment, I don't see how this could properly work. I'm not sure translators would like git commands.
To those who have already implemented a translation process : how did you do ? how do you organize it ?
Thanks a lot
I used JMSTranslationBundle https://github.com/schmittjoh/JMSTranslationBundle to manage my translations on a project. You use the usual |trans on the variables you want to translate. And then you use a command with app/console for this bundle, and it builds your yml files for you (you have the option to pick the output file type). It also has a translation UI. And if you add new words, no problem, you just rerun the command, it adds the new words to your existing words without losing the original translation.
I'm using only YAML files, but they are located in each bundle, not in the app folder. This is the better way if you want to get clean code. If your translation files are in the app folder, you need to extract the content for a bundle you want to use in another project.
If you use a good IDE like PhpStorm, YAML is no problem and the easiest way to edit YAML. Don't forget the symfony plugin for PhpStorm!
EDIT:
You can also use this bundle. It provides a web interface for translations: http://jmsyst.com/bundles/JMSTranslationBundle#documentation
I have implemented manageable translations for one of my project you can refer to My answer similar to this topic what i have done i have created a service which loads all the translations from my repository.I have 2 entities for this
Language
Language entity holds all the languages which has 2 properties locale and name
locale (en,fr,ar ...)
name (english,french ...)
LanguageTranslation
LanguageTranslation entity holds all the translations with properties
catalogue
translation
language (translation language )
languageToken (key for translation)
Now all your translations are cached by symfony in prod/dev folder so each time when user updates or adds new translation i have cleared translations cache programmatically i was using sonata admin so i added a call to this function in prePersist and preUpdate you can also use doctrine event listeners prePersist/preUpdate for this
private function clearLanguageCache(){
$cacheDir = __DIR__ . "cache folder path"; /** path to app/cache*/
$finder = new \Symfony\Component\Finder\Finder();
$finder->in(array($cacheDir . "/dev/translations", $cacheDir . "/prod/translations"))->files();
foreach($finder as $file){
unlink($file->getRealpath());
}
}
This part is missing in my previous answer for clearing cache

Where to place Application bundles?

I have an application written with Symfony and want to merge some small bundles as part of that app.
Regarding the quick Symfony2 bundle tour from Sonata, I need an ApplicationBundle.
quick Symfony2 bundle tour:
There are two types of bundles:
Application Bundle (AB),
Vendor Bundle (VB), that should not be modified inside a project.
The AB directory is where developers implement the project requirements,
An AB can overwrite almost everything from a VB, example: you can redefine a VB template at the AB level.
(Source)
Symfony documentation says about architecture:
The directory structure of a Symfony application is rather flexible,
but the recommended structure is as follows:
app/: the application configuration;
src/: the project's PHP code;
vendor/: the third-party dependencies;
web/: the web root directory
(Source)
But where shall I place it?
There are two possibilities:
app/Application (Sonata EasyExtends places ApplicationBundles here)
src
But what is the right one?
And why are there two possibilities?
The Symfony documentation doesn't cover ApplicationBundle.
Thanks for any hints.
The documentation say that :
Application Bundles (in 2 words, this is important) are stored in src/ directory.
Vendor Bundles are stored in vendor/directory (and came from your composer.json dependencies).
You can (and should) create many bundles for you application.

How can I organize the routes in routes.yml?

I started converting a legacy app over to symfony, my first experience with a php framework was zend, learning zend 1.* to a point where I felt comfortable took me over a year, now I've challenged myself to learn symfony since I find zend 2.* overly complicated.
With zend I was used to just creating a controller class and the view to create a page. With symfony I have to create the route entry, then the controller class and finally the view.
My routes.yml is over 100 lines long with all the requirements, methods and whatnot, and I am about a third into the project.
How can I keep this file organized? Right now it looks very chaotic.
Your site should be split into bundles that represent the section of the site that it concerns (from the site I am currently working on)
AddressingBundle
ChartBundle
ContactBundle
CoreBundle // A bundle that contains all merging items (model, types, etc)
CustomerBundle
PaymentBundle
PolicyBundle
.. etc ...
NOTE: I use YAML for routing. Annotation are available that require no extra files but (possible) means lack of reusability of routes
Within each of those bundles is a set of routing files that can be included or not. Each bundle has a routing file located at Resources/config/routing.yml that links to a selection of files located in Resources/config/routing/.
Then for the main app/config/routing.yml file I can then link to the outer (Resources/config/routing.yml) or internal (Resources/config/routing/something.yml) as needs be. Also it enables you to use the same routes multiples times using different paths depending on prefixes.
UPDATE:
The idea of bundles is to separate concerns. So for me I have (which I have massive stolen from the make up of Sylius) the CustomerBundle that only has a single entity (Customer), then I have the AddressingBundle with a single entity (Address). These can work alone but are attached using the data in CoreBundle. This way if I need to edit the Customer section I just edit CustomerBundle and make sure it interacts in the same way. Technically there is not public and private bundles (due to them all being below root), there are only routes that you make public via your routing and security.

Resources