cezerin icon indicating copy to clipboard operation
cezerin copied to clipboard

Multilingual cezerin

Open ceracera opened this issue 6 years ago • 29 comments

I know you can set up a shop with different languages thanks to many translations done already but that is still a single language shop. I would like to make shop available in multiple languages. Product descriptions, titles, attributes, etc. would be written in other languages in addition to primary language and visitors could choose shop language they want.

Are there any plans for it? What would be the right way to implement that feature on cezerin?

ceracera avatar Aug 01 '18 08:08 ceracera

@ceracera does this answer your question:

https://github.com/cezerin/cezerin/issues/523

amp9020 avatar Aug 01 '18 15:08 amp9020

@ceracera Adding multilingual to Cezerin will take around 1-2 month roughly. Multilingual is a pretty complex task. Mostly for UI. Language switchers everywhere.

I think the current goal should be stable v1 with the customer account. @restmount am I right?

sonufrienko avatar Aug 01 '18 15:08 sonufrienko

@amp9020 No, I was thinking more like multiple languages at the same time

ceracera avatar Aug 01 '18 21:08 ceracera

@sonufrienko Yes, I understand that. The thing is that I would like that feature available really soon so I was thinking in doing some of it, at least some core functionality, myself. Can you point me in the right direction about how to deal with it. Something like a description of what needs to bi changed/added in order to achieve that functionality.

ceracera avatar Aug 01 '18 21:08 ceracera

@ceracera

Steps for the multilingual task

  1. Design database schema
  2. Modify API to accept language in the header
  3. Modify API to work with right locale
  4. Add language option to CezerinClient (REST API client)
  5. Dashboard: add a language switcher
  6. Store: add a language switcher
  7. Theme: change source of locale theme/locales/.json

The Store is almost headless. So the most modification is needed to do in DB, API, and dashboard. Tips about db schema - Approaches to JSON internationalisation

sonufrienko avatar Aug 02 '18 04:08 sonufrienko

@sonufrienko

Thanks a lot. That is great help. I'll try to make some basic functionality this weekend and if you have some additional hints and advice, please let me know.

ceracera avatar Aug 02 '18 20:08 ceracera

Ok.

sonufrienko avatar Aug 02 '18 20:08 sonufrienko

@sonufrienko I have trouble finding the right way to pass 'Accept-Language' header to server api. Can you point me in the right direction?

I would like to read Headers when there is no Local storage, and Local storage when user already picked a language before. I have found where are options in cezerinClient but I don't know how to pass 'Accept-Language' header to those options. Would it be ok if I could pass header as an option to api cezerinClient in src/api/server/ajaxRouter.js and src/store/server/api.js?

It would be great if you could help :)

Thanks

ceracera avatar Aug 05 '18 04:08 ceracera

@ceracera need to modify CezerinClient at https://github.com/cezerin/client/blob/master/src/apiClient.js#L15 and put language in the header to fetch.

sonufrienko avatar Aug 05 '18 08:08 sonufrienko

on the server

  1. on page rendering you need to read all locales and put to Theme
  2. every router should read language from the header and put to service to operate with MongoDB

on the client

  1. Theme should receive all locales and find specific

SSR adds complex to this task.

sonufrienko avatar Aug 05 '18 08:08 sonufrienko

@sonufrienko Thanks a lot. But it's hopeless :( I still cannot figure out how to get user language to CezeringClient. I can read Local storage in browser but since it's shared code on server also, it doesn't work...

What I did is this:

  • Changed the db schema
  • Modified CezerinClient like you said (as local npm package)
  • Updated methods on src/api/server/ that get translateble fields

It's all working well when I explicitly add eg. {language: "fr"} to CezerinClient options but I cannot get it to pass Language headers or Local storage items.

ceracera avatar Aug 05 '18 13:08 ceracera

@ceracera

  1. add setLanguage(<locale>) to CezerinClient - it should add language to the header on fetch request to API and AJAX (important - both).
  2. call api.setLanguage(<locale>) or api.ajax.setLanguage(<locale>) when client switch language
  3. also need to check is this header allowed

At this point, your CezerinClient (client and server) will always know your current language and attach it to every fetch requests.

  1. API and AJAX router should get language from the header and put to services
  2. AJAX should use language header to access API

Take a point that AJAX is just a proxy to API from the client side.

sonufrienko avatar Aug 05 '18 13:08 sonufrienko

@sonufrienko Can you give me more info on number 1. above. Some example, pseudo code?

ceracera avatar Aug 05 '18 14:08 ceracera

@sonufrienko DB schema and server methods for getting right language are working. But only when I literraly type language header in getConfig method of apiClient. I don't understand how can this value be set by setLanguage function. Do I have to use options in CezerinClent, like the token is passed now?

Any help is more than welcome :)

ceracera avatar Aug 05 '18 18:08 ceracera

@ceracera you mean this 1.

class AjaxClient {
   constructor(options) {
      this.locale = "en";
class AjaxClient {
   setLocale(locale) {
      this.locale = locale;
class AjaxClient {
   getConfig(method, data, cookie) {
      let config = {
         headers: {
            'Accept-Language': this.locale

sonufrienko avatar Aug 06 '18 09:08 sonufrienko

@sonufrienko Great, thanks, done that. I can call setLocale from src/store/client/api, but how can I pass user language from client to src/api/server/ajaxRouter.js or src/store/server/api.js? I don't know how to pass localStorage item to ajaxRouter...

ceracera avatar Aug 06 '18 14:08 ceracera

on Theme (client-side)

if (typeof window !== 'undefined') {
   api.ajax.setLocale(localeFromLocalStorage)

CezerinClient will attach headert to every fetch requests.

on ajaxRouter.js

  1. get Accept-Language from req header
  2. api.products.list(filter, localeFromHeader) for example

sonufrienko avatar Aug 06 '18 14:08 sonufrienko

@sonufrienko Ok, so on ajaxRouter.js (src/api/server/ajaxRouter.js, right?), when I get lang header it gets passed back to CezerinClient and there, in the list(filter) method, for example, I have to pass that header to api.

From there I can pick a language in, for example, getProducts method from src/api/server/services/products/products.js. Is that right?

Sorry to bother you but it's difficult to me to make sense of this since it has many componenets...

ceracera avatar Aug 06 '18 15:08 ceracera

@sonufrienko I managed to make it work. It's only for product names for now but it's a start :)

Here is what I did:

On CezerinClient

  • added language to api and ajax clients in constructor and header for language in getConfig
  • added setLanguage method on both api and ajax clients

On API

  • in ajaxRouter get language header and call setLanguage(language) in CezerinClient with it. (only for get('/products') route for now)
  • in routes/products.js get language header and pass it to ProductsService.getProducts.
  • in services/products/products.js use language to pick right value based on db schema (item name = item.name[language])

On Store

  • in server/loadState.js get cookie with language and use it to call setLanguage(language) in CezerinClient.
  • edit server/themeLocales.js to map through all available languages (hard coded for now) and get all locales
  • also in server/loadState.js use cookie language to modify theme text to get it in right language (themeText: themeText[language])

On Theme

  • in components/header/index.js make language switch that sets the cookie
  • in components/products/custom.js, when fetching products, use cookie to get right language

Now all translatable fields need to be changed like what is now done for products (now is just product name, though). I thought maybe to use traverse like https://www.drzon.net/posts/approaches-to-json-internationalisation-i18n/ so that no change have to be made on routes and services. Just traverse the resulting json for whatever resource and get the right one back to client.

What do you think? Maybo I can submit a pull request so that you and others can contribute?

ceracera avatar Aug 07 '18 09:08 ceracera

@ceracera Awesome! Give me some time to think about it.

sonufrienko avatar Aug 07 '18 09:08 sonufrienko

Now I added traverse to CezerinClient, in get method. So if there is cookie language: fr and data in mongo is like:

{
  name: {
    en: "Shirt",
    fr: "Chemise'"
  } ...

it is fetched as:

{name: "Chemise"} ...

It gets everything translated, products, categories, pages... Problem is updating stuff. What would be the right way to do similar thing for put and post in admin area? Maybe to define translatable fields (there are not many of them) and intercept db methods or CezerinClient to assign parent object on updating those fields. Eg. if you are updating {name: Shirt} and you switched language to English, db should write it as {name: {en: "Shirt"}}. @sonufrienko What do you think?

ceracera avatar Aug 10 '18 01:08 ceracera

@ceracera do you have the code accessible anywhere?

CezarManea avatar Sep 01 '18 17:09 CezarManea

@CezarManea Its not finished yet

ceracera avatar Sep 03 '18 07:09 ceracera

@ceracera did you manage to get the changes to some stable point? what is the status of the work? thanks

jferencik avatar Sep 21 '18 08:09 jferencik

No, I gave up :( I had trouble with admin part and did not have the time to work on it. Maybe later. I will post here.

ceracera avatar Sep 21 '18 08:09 ceracera

Great job @ceracera, please give a sign if you have made a pull request, thx!

lortschi avatar Oct 15 '18 07:10 lortschi

https://join.slack.com/t/caesarcommerce/shared_invite/enQtNDU5OTU4ODIyMzUyLWU2NWRmMzI3MTMxYTlkODFkZTdmZTM4MjdiNWQ2NTg4MzIyNDE1Yzc0ZGY5ZmFkNDc2Njc5Y2NkNWQxNDk4ODM On Mon, Oct 15, 2018 at 12:55 AM Lortschi [email protected] wrote:

Great job @ceracera https://github.com/ceracera, please give a sign if you have made a pull request, thx!

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/cezerin/cezerin/issues/527#issuecomment-429744034, or mute the thread https://github.com/notifications/unsubscribe-auth/AA-oh9G8IiAz9GJ-WnvfAwiv3ZoFvqhhks5ulD9mgaJpZM4VqDX2 .

dougkulak avatar Oct 20 '18 15:10 dougkulak

@dougkulak this not an answer to the issue, you already posted that you opened a slack channel. stop spamming the issues.

amp9020 avatar Oct 20 '18 18:10 amp9020

Hi guys, any further plans regarding language switcher? Thanks

IvanLjubicic avatar Dec 07 '18 20:12 IvanLjubicic