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

Support for auto-loading vendor packages language paths #181

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

Haruki1707
Copy link
Contributor

This pull request introduces a feature to automatically load all language paths from vendor packages directly from the vendor folder without publishing them. Previously if you didn't want to publish the package lang files, the packages paths had to be manually specified in the additionalLangPaths option.

To maintain compatibility for those already adding the paths manually, this feature is disabled by default. It can be enabled by setting the loadPackagesLangPaths option to true in the Vite plugin options.

An added benefit of using this new feature is that the generated translation key will align with the key used by Laravel in PHP, offering a more consistent and streamlined approach.

Additionally, the README.md has been updated to include the new loadPackagesLangPaths option.

@xiCO2k
Copy link
Owner

xiCO2k commented Aug 21, 2024

Hey @Haruki1707 really nice PR, can you create like a dummy repo for me to try that with some vendor languages?

Sorry for the delay on this.

Thanks.

@plokko
Copy link

plokko commented Nov 27, 2024

Please could you close this PR? I need this 👍

Tested on a Inertia/Laravel 11 project and seems to work fine (with a package i developed)

@plokko
Copy link

plokko commented Dec 10, 2024

Any plans on closing the issue?
The code seems to be working fine in my tests and i do need vendor package resolution.

@Haruki1707
Copy link
Contributor Author

Hi @plokko,

Sorry, I haven’t had the chance to finish this pull request yet.

The main issue I encountered is with determining the location of language files for packages and libraries. If a package registers its language files in the Service Provider following Laravel's conventions (e.g., placing them in the lang folder), everything works smoothly. However, if the files are registered elsewhere, we’d need to inspect the Service Provider to locate them.

Additionally, Laravel allows for customizing the key used to reference language files. By default, language strings can be accessed via packageName::lang.string, where packageName is derived from the namespace of the Service Provider. However, a package can override this by passing a custom name as the second argument during registration, resulting in something like customName::lang.string. Detecting such changes programmatically is tricky.

To complicate things further, some packages (like Spatie’s) extend Laravel's base Service Provider to add custom functionality. This abstraction often changes how they register language files, making it nearly impossible to reliably determine this information solely through JavaScript.

A potential solution would be to implement a small piece of PHP code to extract this information directly from Laravel and provide it as a JSON or similar format for this package. However, not sure if this would deviate from the current design goal of handling everything on the JavaScript side in this i18n package.

Some help with solving this would be greatly appreciated!

@plokko
Copy link

plokko commented Dec 18, 2024

s further, some packages (like Spatie’s) extend Laravel's base Service Provider to add custom functionality. This abstraction often changes how they register language files, making it nearly impossible to reliably determine this information solely through JavaScript.

Yeah i was thinking the same:
even if the default locale folder is lang under the package root there is NOT any way to know where the locale files are located; it may be possible with some fiddling with PHP but the library does some JS interpolation so it may not work every time as you said and there is no standard way to specify it as an extra field of the composer.json of the package like aliases.

I have a proposition to fix the issue:
by default we search lang folder on the package root (maybe we can search for trans or other usual folders if lang is not present) but we provide a way to manually resolve the path for a package in the settings.

A brief example:

//...
        i18n({
            // This will force locale path for vendor packages
            resolveVendorLocales: {
                'plokko/example-package' : 'path/to/lang' //this points to <root_path>/vendor/plokko/example-package/path/to/lang
            },
            
        }),
//...

We may even add a package alias like this

//...
        i18n({
            // This will force locale path for vendor packages
            resolveVendorLocales: {
                'plokko/example-package' : {
                   alias: 'customName', // will be mapped to customName, ex: customName::lang.string
                   path: 'path/to/lang' //this points to <root_path>/vendor/plokko/example-package/path/to/lang
            },
       
        }),
//...

@Haruki1707
Copy link
Contributor Author

Before completely giving up on retrieving the data directly from Laravel, I experimented with creating a standalone PHP script to extract vendor language information. Here's what it looks like:

<?php

require __DIR__.'/../../../../vendor/autoload.php';

// Bootstrap the Laravel application
$app = require_once __DIR__.'/../../../../bootstrap/app.php';

// Access the console kernel to initialize Laravel (this mimics the artisan behavior)
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);

// Ensure that Laravel is booted by running a `dummy` command (this is like executing artisan commands)
$kernel->bootstrap();

// Laravel application is now booted and can execute any laravel code as artisan commands would

$translationLoader = $app->make(Illuminate\Translation\Translator::class)->getLoader();

if ($translationLoader instanceof Illuminate\Contracts\Translation\Loader) {
    exit(json_encode($translationLoader->namespaces()));
} else {
    exit(1);
}

I tested executing this script using Node.js's execSync function, and it worked as expected:

const scriptPath = process.cwd() + '/node_modules/laravel-vue-i18n/src/utils/packages-translation-namespaces.php'
const result = execSync('php ' + scriptPath)

return Object.entries(JSON.parse(result.toString()))
    .map(([key, value]) => ({
      name: String(key),
      langPath: String(value)
    }))

However, using this approach would require mocking the script's response during testing, as we don't have access to a real Laravel application in the test environment. That said, since this method relies on Laravel's well-established internal mechanisms for registering language files, it's unlikely to break or change unexpectedly.

Before proceeding, I’d like your thoughts on executing this PHP script, @xiCO2k. If this approach isn’t ideal, I’ll move forward with @plokko’s proposed solution, even though it could be a bit tedious for users working with packages that don’t follow the expected structure.

@xiCO2k
Copy link
Owner

xiCO2k commented Dec 20, 2024

Thanks @Haruki1707 I don't like it too much, because of OS support, so if we can keep it with the js parser will be great.

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

Successfully merging this pull request may close these issues.

3 participants