Looks like all the problems with translations come from the fact, that $locale parameter for the repository methods for products and taxons is always the default locale - it does not change when you switch the language. So it always is putting "en_US" and ignores the selected language.
Also, when you need to pass a locate to your own created repository method, you have to do that through routing config and when using locale: %locale% - I too get the default locale config option.
Are you using Beta 1 tag? I believe we have fixed this already, cc @GSadee.
Yes, Beta 1
I'm experiencing something quite different about Taxon Locale.
When Sylius is building the Taxon menu, the slugs don't depends on the selected locale but depends on the Channel default locale.
And if I want to access to a slug associated with the default channel locale, it works well, but if the slug is in a different locale, it does not work.
For the second point it seems to come from this file:
#vendor/sylius/sylius/src/Sylius/Bundle/ShopBundle/Resources/config/grids/product.yml
sylius_grid:
grids:
sylius_shop_product:
driver:
name: doctrine/orm
options:
class: "%sylius.model.product.class%"
repository:
method: createQueryBuilderByChannelAndTaxonSlug
arguments:
channel: "expr:service('sylius.context.channel').getChannel()"
taxonId: $slug
locale: "expr:service('sylius.context.channel').getChannel().getDefaultLocale().getCode()"
Which was previously:
sylius_grid:
grids:
sylius_shop_product:
driver:
name: doctrine/orm
options:
class: "%sylius.model.product.class%"
repository:
method: createQueryBuilderByChannelAndTaxonSlug
arguments:
channel: "expr:service('sylius.context.channel').getChannel()"
taxonId: $slug
locale: '%locale%'
I my case I have a multi-language store, single channel, so I expect when I switch the language, translated slugs are used to navigate and set a proper language for the user. Because we don't have language in the URL. Also, this is important SEO stuff.
@psihius it's sylius code, In fact I don't know how to select current active language here
Found it:
- "expr:service('sylius.context.locale').getLocaleCode()"
@pjedrzejewski Shouldn't the argument given by @psihius be the one used in the grid's repository argument ? Using the channel's default locale as it currently is implemented leads to translation issues in the shop.
If I change the grid definition to:
sylius_grid:
grids:
sylius_shop_product:
driver:
name: doctrine/orm
options:
class: "%sylius.model.product.class%"
repository:
method: createQueryBuilderByChannelAndTaxonSlug
arguments:
channel: "expr:service('sylius.context.channel').getChannel()"
taxonId: $slug
# Modified line : use current locale instead of default locale
locale: "expr:service('sylius.context.locale').getLocaleCode()"
everything works fine, products names and slugs are correctly translated.
I tried to override this in my own config, but as stated in issues #7292 and #7095, it is not possible to override anything from the "driver" sections of grid definition. Plus, it really makes sense to me that everything that is locale related should use the current locale and not the channel default locale.
Am I missing something ? There must be a good reason as to why it was implemented this way in the first place ?
@jurv I think you are right, the only problem is that there is no guarantee that products have names filled for these locales, so the products without translations in this locale will skipped. Still, I think that should be default behavior so it is not confusing and everyone can adjust the logic to their use-case when we fix the bug with overriding grid configs.
@pjedrzejewski I'm on current dev-master and now it broke the product listing completely, because of
taxon: "expr:service('sylius.repository.taxon').findOneBySlug($slug, service('sylius.context.channel').getChannel().getDefaultLocale().getCode())"
in sylius/src/Sylius/Bundle/ShopBundle/Resources/config/grids/product.yml
You can't use a default locale for the channel, because it filters out any chance of getting the taxon by a translated string.
But I have an idea - why not use a subquery to find the taxon, and in the parent query select available translation?
Something like
SELECT * FROM sylius_taxon AS sqt
LEFT JOIN sylius_taxon_translations AS sqtt ON sqt.taxon_id = sqtt.translatable_id
WHERE sqtt.locale IN(:current_locale, :default_locale)
ORDER BY FIELD(sqtt.locale, :current_locale, :default_locale)
LIMIT 1
@TheMadeleine WDYT about the query above? :)
As stated here: http://stackoverflow.com/questions/9378613/how-to-define-a-custom-order-by-order-in-mysql
You may want to use a CASE instead of FIELD() for compatibility reasons
@pjedrzejewski
Ideally, ofc, this probably needs a good rework to handle in the objects falling back to default locale if the current locale data is empty, but SQL way is quite a bit faster to implement, and actually, to think about it, maybe the same solution in the end, just done another way. This, however, will not handle the case if, for example, there is an empty title for the current locale for the product - it will not be able to show the default product name (or any other field for that matter). But I think the way translations work right now - you either have a translation record in database or don't, right? So, does not really matter in that case, as if I remember correctly, you can't add a translation without adding data to the fields anyway.
As for product listings, they also need to handle somehow fallback to default translation. Probably the same way will work, the only concern will it impact performance or not (maybe will need a SELECT FROM SELECT to avoid custom ORDER BY on large unfiltered data sets).
Why won't we just change
taxon: "expr:service('sylius.repository.taxon').findOneBySlug(
$slug,
service('sylius.context.channel').getChannel().getDefaultLocale().getCode()
)"
into
taxon: "expr:service('sylius.repository.taxon').findOneBySlug(
$slug,
service('sylius.context.locale').getLocaleCode()
)"
Only problem is that will hide products which do not contain translation for current locale. Unless we handle the fallback.
Well, we have the channel passed to the method anyway, we can pass the context locale code (ot the whole context service) and try the query approach I described earlier.
After thinking it through, I think this is actually a good solution, because if you add a translation, the validation does not allow you to leave fields empty in admin panel. And you cannot add a product without adding anything to default locale. Looks like a WIN-WIN, provided it does not impact performance too much.
Well, scratch my previous hint on CASE WHEN - that actually does not work with WHERE - it filters out languages if taxon slug is different in all languages, maybe it will work with having, but I found it easier just do a subquery like this.
What I actually came to right now and that works correctly for taxons, is this
public function findOneBySlug($slug, $locale)
{
$subQuery = $this->createQueryBuilder('subq')
->select('subq.id')
->innerJoin('subq.translations', 'subq_trans')
->andWhere('subq_trans.slug = :slug')
;
return $this->createQueryBuilder('o')
->addSelect('translation')
->innerJoin('o.translations', 'translation')
->andWhere($subQuery->expr()->in('o.id', $subQuery->getDQL()))
->andWhere('translation.locale = :locale')
->setParameter('locale', $locale)
->setParameter('slug', $slug)
->getQuery()
->getOneOrNullResult()
;
}
Same, probably, goes for every SluggableAware translatable entity.
Maybe, actually, we still need a custom order by and where locale IN(:current, :default) instead of just where locale = :current - that way it will fall back to default locale if product does not have a translation.
Subquery is required because we need to select the proper language on first pass - otherwise the Doctrines unit of work selects the taxon once and you are left with taxon having wrong translation and slugs - this resulted in whole page correctly being translated, but the taxon requested has slug and content for the language the slug is - that leads to weird things and wtf's
This issue has been automatically marked as stale because it has not had any recent activity. It will be closed in a week if no further activity occurs. Thank you for your contributions.