Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

multiple languages per domain #2418

Closed
3 of 4 tasks
GalV8 opened this issue Sep 16, 2023 · 11 comments · Fixed by #2705
Closed
3 of 4 tasks

multiple languages per domain #2418

GalV8 opened this issue Sep 16, 2023 · 11 comments · Fixed by #2705

Comments

@GalV8
Copy link

GalV8 commented Sep 16, 2023

Describe the feature

add ability to specify prefix for each language on top of domain.

        locales: [
            {
                code: 'sl',
                domain: 'domain1.com',
            },
            {
                code: 'hr',
                domain: 'domain1.hr',
            },
            {
                code: 'hu',
                domain: 'domain2.com',
                prefix: '/hu'
            },
            {
                code: 'en',
                domain: 'domain2.com',
                prefix: '/en'
            },
    ]

so paths would look like this:

domain1.com => sl lang
domain1.hr => hr lang

domain2.com/en => en lang
domain2.com/hu => hu lang

if user hits "domain2.com" it redirects him to "domain2.com/defaultLocale"

i am willing to implement this myself but i need a bit of guidance since i am new to nuxt / ts :)

Additional information

  • Would you be willing to help implement this feature?
  • Could this feature be implemented as a module?

Final checks

@WooHoo-Wu
Copy link

I also have this problem and want to know how to solve it

@GalV8
Copy link
Author

GalV8 commented Nov 10, 2023

I made a workaround BUT its a bit of a hack, if you enable it you will experience text flickering sometimes (solved the flickering).. idk why but it will flicker from defaultLocale to selectedLocale

@BobbieGoede sorry for tag :) but do you have any idea why this is happening? this is the only issue i have with this workaround

What i did was i set strategy to "no_prefix" added pages:extend hook for all locales that require prefix:

const cornerLocales = (process.env.APP_CORNER_LOCALES).split(',');
  hooks: {
      'pages:extend'(pages) {
          pages.forEach(page => {
              cornerLocales.forEach(locale => {
                  pages.push({
                      ...page,
                      path: `/${locale}${page.path}`,
                      name: `${locale}-${page.name}`,
                  });
              })
          });
      },
  },

    i18n: {
        strategy: 'no_prefix',
        defaultLocale: process.env.APP_DEFAULT_LOCALE,
        locales: [...locales...],
        lazy: true,
        langDir: 'lang/',
        detectBrowserLanguage: false,
        differentDomains: false,
        compilation: {
            strictMessage: false,
        },
    },

made plugin 01.loadLocale.ts

export default defineNuxtPlugin(async (nuxtApp) => {
    const localeStore = useLocaleStore();

    let host: string = "";
    let path: string  = "";
    if (process.server){ //todo make .server / .client
        // @ts-ignore
        host = nuxtApp.ssrContext?.event.node.req.headers.host;
        // @ts-ignore
        path = nuxtApp.ssrContext?.event.node.req.originalUrl;

        const result: any = await localeStore.boot(host,path);
    }else{
        host = window.location.host;
        path = window.location.pathname;

        const result: any = await localeStore.boot(host,path);
    }
});

made pinia store localeStore with boot function and userLocale (i use this var when i want to know what lang user has)

    async function boot(host: string, path: string){

        switch (host){

            case config.public.suURL:
                userLocale.value = "sl";
                break;
            case config.public.cornerHrURL:
                userLocale.value = "hr";
                break;
            case config.public.cornerDeURL:
                userLocale.value = "de";
                break;
            case config.public.cornerURL:
                let selected_locale = getLocaleFromUrl(path);

                if (corner_locales.includes(selected_locale)){
                    userLocale.value = selected_locale;
                }else {
                    userLocale.value = "en";
                }
                break;
        }

        await $i18n.setLocale(userLocale.value);

        return userLocale
    }

    function getLocaleFromUrl(path: string){
        let segments = path.split('/');
        let selected_locale = segments[1];

       if (!corner_locales.includes(selected_locale)){
            for (let segment of segments){
                if (corner_locales.includes(segment)){
                    selected_locale = segment;
                    break;
                }
            }
        }

        return selected_locale;
    }

navigateTo composable:

export const navigateToPath = (path: string, options: any = {}): any => {

    if (options.external == true){
        return navigateTo(path, options);
    }

    const config = useRuntimeConfig();
    const corner_locales = config.public.cornerLocales;
    const localeStore = useLocaleStore();
    const locale = localeStore.userLocale ?? config.public.defaultLocale;

    let url = "";
    if (corner_locales.includes(locale)) {
        url = `/${locale}/${path}`;
    } else {
        url = `/${path}`;
    }

    console.log("navigating to path:", url, options);
    return navigateTo(url, options);
};

export const navigateToDifferentDomain = (path: string, newLocale: string): any => {
    const config = useRuntimeConfig();
    const corner_locales = config.public.cornerLocales;
    const localeStore = useLocaleStore();
    const currentLocale = localeStore.userLocale ?? config.public.defaultLocale;

    let segments = path.split('/');
    let newUrl = "";
    let host = "";

    if (corner_locales.includes(newLocale)) {
        host = config.public.cornerURL;

        if (corner_locales.includes(currentLocale)) {
            segments[1] = newLocale;
        } else {
            segments[0] = newLocale;
        }

    } else {

        if (newLocale == "hr") {
            host = config.public.cornerHrURL;
        } else if (newLocale == "de") {
            host = config.public.cornerDeURL;
        } else {
            host = config.public.suURL;
        }

        if (corner_locales.includes(segments[1])) {
            segments[1] = "";
        }

    }

    if (corner_locales.includes(segments[1]) && segments[2] == "" && segments[3] == undefined){
        segments.shift()
        for (const segment of segments) {
                newUrl += '/' + segment;
        }
    } else{
        for (const segment of segments) {
            if (segment != "") {
                newUrl += '/' + segment;
            }
        }
    }

    return navigateTo(window.location.protocol + "//" + host + newUrl, {
        external: true
    });
};

then you need a global middleware to make sure locales that shouldnt be accessible on certiant domains are not.. ex:

domain1.com cant have hu,it prefixes so if url contains hu,it at the start redirect him to 404 not found.. would paste code but there is alot of code that is irellevant here.. also if user gets redirected to error.vue you need to navigate user yourself

@BobbieGoede
Copy link
Collaborator

idk why but it will flicker from defaultLocale to selectedLocale
...
What i did was i set strategy to "no_prefix"

On request the module uses the configured strategy, locales and defaultLocale (and more) to navigate/redirect users and load the appropriate locale messages. If it is detected that a certain locale should be loaded, the module will also set the locale, I can only guess, but I think your code and the module code are fighting to switch locale 😅..

You might have more luck by setting skipSettingLocaleOnNavigate: true but then you would also need to handle language switching yourself.

I can see the value of this feature but it I think would be quite complex to implement with the way the module is built currently. You might have better results by splitting the i18n configs for domain1 and domain2 into separate nuxt layers, and using environment variables to extend one of them on build. But that has the drawback of having to build twice and switching between languages during development would be a hassle.

@GalV8
Copy link
Author

GalV8 commented Nov 11, 2023

Thanks for response :)

I had some time and played around a bit more.. I've updated package to latest version had beta before, enabled lazy mode and i am no longer experiencing flickering.. idk what fixed it but maybe its because i have more content getting loaded and it has time to init lang bundle 😅? (flickering was only at the start from default to selected, after that everything was fine just on the inital load)..

so yeah it is possible to use multiple langs per domain you just need to hack it a bit :p.. I have updated my answer with full code if anyone wants to try :)

@sxhweb
Copy link

sxhweb commented Nov 14, 2023

I have the same problem, is there any solution?

@sxhweb
Copy link

sxhweb commented Nov 14, 2023

Setting differentDomains does not solve this scenario

Copy link
Contributor

I am in a need for similar feature and couldn't find any up-to-date workaround so thought I would have a go for it myself.
My suggested implementation only requires setting a domainDefault: true on the default/fallback locale for each domain (as opposed to the suggested prefix: '/hu' in this issue).
It should support the various strategies except no_prefix as that does not make any sense.

You can see the changes in this commit: bjerggaard@d610dd6
Let me know of any feedback/improvements and I will be happy to create a pull request.

This feature did break some of the differentDomains tests and I couldn't get my head around the reason for following line in the current code:

return !options.differentDomains && DefaultLocalizeRoutesPrefixable(opts)

Perhaps @kazupon can shed a light on this? For me, it looks like no_prefix is implied when using differentDomains in the current codebase.

@e-kravtsov
Copy link

e-kravtsov commented Mar 10, 2024

I am in a need for similar feature and couldn't find any up-to-date workaround so thought I would have a go for it myself. My suggested implementation only requires setting a domainDefault: true on the default/fallback locale for each domain (as opposed to the suggested prefix: '/hu' in this issue). It should support the various strategies except no_prefix as that does not make any sense.

You can see the changes in this commit: bjerggaard@d610dd6 Let me know of any feedback/improvements and I will be happy to create a pull request.

This feature did break some of the differentDomains tests and I couldn't get my head around the reason for following line in the current code:

return !options.differentDomains && DefaultLocalizeRoutesPrefixable(opts)

Perhaps @kazupon can shed a light on this? For me, it looks like no_prefix is implied when using differentDomains in the current codebase.

Thank you for your PR.
I think prefix is needed for the situation when you have en as a second language for all of your domains and code is used as id

locales: [
      {
        code: 'en',
        file: 'en',
        prefix: '',
        domain: 'mydomain.com',
        domainDefault: true
      },
      {
        code: 'pl',
        prefix: 'pl',
        domain: 'mydomain.com'
      },
      {
        code: 'es',
        prefix: '',
        domain: 'es.mydomain.com',
        domainDefault: true
      },
      {
        code: 'en-ES',
        file: 'en',
        prefix: 'en',
        domain: 'es.mydomain.com',
      },
      {
        code: 'fr',
        domain: 'fr.mydomain.com',
        domainDefault: true
      },
      {
        code: 'en-FR',
        file: 'en',
        prefix: 'en',
        domain: 'fr.mydomain.com',
      },
    ]

Then the solution will cover most of usecases

@bjerggaard
Copy link
Contributor

I have updated the PR with latest changes from main branch.
However I haven't got time to look into @e-kravtsov suggestion of including the 'prefix' property.

@BobbieGoede
Copy link
Collaborator

@bjerggaard
Thanks for updating the PR, sorry I haven't taken the time yet to review it even though it's been open for a while. Since it's up to date again I hope to give it a good look soon when I have the time!

@bjerggaard
Copy link
Contributor

@BobbieGoede appreciate it, thanks :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
6 participants