Open TotalWipeOut opened 5 months ago
@TotalWipeOut First: Thank you very much for your help!
Some comments:
I decided to use the term 'placeholders' instead of 'parameters' so that it is less ambiguous
A placeholder is in the message: "Hello {name}!" and the process of converting the message with a placeholder can be called formatting or replacement. To format or replace the placeholders, values/parameters are required. Therefore, the naming with name "placeholders" in your code is incorrect.
As I really didn't want a BC break, I have hijacked the
$textDomain
parameter of thetranslate
/translatePlural
method to allow passing in placeholders where it seemed most logical going forward.
Hijacking is not an option because:
string
https://github.com/laminas/laminas-i18n/blob/b3259acf50899468cbbd105539ed0d5a5175c4d8/src/Translator/TranslatorInterface.php#L14
- Support multiple kinds of placeholder formats
My first thought was, why allow multiple variants when one official is enough, but if we see this component in use with different frameworks or CMS, this would be a benefit.
On the other hand, with the ICU MessageFormat the translation with plural can be done with one method, and we can kill translatePlural
.
- No new view helpers
Why not? We can update the view helper configuration to use the new view helpers with parameter support:
And the new view helper(s) can be used like this:
<?= $this->translate($message, params: ['name' => 'World']) ?>
The old view helpers must be retained for reasons of backward compatibility.
@froschdesign Thanks for explaining placeholders vs parameters. I will adjust the code naming.
The reason for multiple placeholder formats is that while I agree ICU message format is the place to be, i wanted the ability to allow this feature to integrate with existing apps with existing translation files that might do it in the various other ways via custom solutions. The app I work on uses printf format for this task currently
Should I add a new method in Translator/Translator
called translateWithParams
or similar instead?
Regarding view helpers, from your example, can you show me how this will work with a new view helper, but called the same in the view and it not being a BC break? I don't fully understand
Thanks again for your detailed response :heart:
Regarding view helpers, from your example, can you show me how this will work with a new view helper, but called the same in the view and it not being a BC break? I don't fully understand
This is a simple mapping:
$renderer = new Laminas\View\Renderer\PhpRenderer();
$renderer->getHelperPluginManager()->configure(
(new Laminas\I18n\ConfigProvider())->getViewHelperConfig()
);
$renderer->getHelperPluginManager()->setFactory(
NewTranslate::class,
Laminas\ServiceManager\Factory\InvokableFactory::class
);
$renderer->getHelperPluginManager()->setAlias(
'translate',
NewTranslate::class
);
echo $renderer->translate('example', params: ['foo' => 'bar']);
This overwrites the existing view helper, as is done if you want to use your own.
@froschdesign So i understand correctly with this approach, this functionality requires additional config in the app, add wont work out of the box?
If so, for something accessible out of the box, would adding a new 4th param to the Translate
view helper be considered a bc break? As a view helper called translateWithParams
feels too long for something that is used everywhere
I see other frameworks use t
or trans
to keep it short, could we use a name like that?
Thanks again for your time.
So i understand correctly with this approach, this functionality requires additional config in the app, add wont work out of the box?
This component already provides a configuration:
This must be extended so that the new view helper(s) can be used.
If so, for something accessible out of the box, would adding a new 4th param to the Translate view helper be considered a bc break?
No, because a new view helper must be created and the old one remains as it is. (The view helpers are not marked as final, so we cannot change them.)
As a view helper called
translateWithParams
feels too long for something that is used everywhere I see other frameworks uset
ortrans
to keep it short, could we use a name like that?
No, nothing must and will be changed in this case. Via the configuration we will map translate
to the new view helper which supports parameters. It is not necessary to change anything in the view scripts, unless you want to use the parameters, in which case the method call must be extended:
// Before:
$this->translate($message);
// After:
$this->translate($message, params: ['name' => 'World']);
This works out of the box.
Please look at the renderer of laminas-view:
@froschdesign Thanks - I have tried to address your feedback :)
@froschdesign Let me know if this is something your happy to be included in this library, and i will get the tests written. Thanks CC @weierophinney for visibility, as you raised the original issue.
@TotalWipeOut Sorry for the late response! I was busy with this task: #90
Let me know if this is something your happy to be included in this library…
Short: The feature is definitely necessary, but with a modified implementation.
If we add this feature in a minor release then without any changes on the Translator
class.
namespace Laminas\I18n\Translator\Formatter;
use Laminas\I18n\Translator\Translator;
use Laminas\I18n\Translator\TranslatorInterface;
final class TranslatorFormatterDecorator implements TranslatorInterface
{
public function __construct(
private readonly Translator $translator,
private readonly FormatterInterface $formatter
) {
}
public function translate(
$message,
$textDomain = 'default',
$locale = null,
iterable $parameters = []
): string {
// …
}
public function translatePlural(
$singular,
$plural,
$number,
$textDomain = 'default',
$locale = null,
iterable $parameters = []
): string {
// …
}
}
Translator
classHere we can change the interface for the translator and rework the entire Translator
class.
So if you can wait, the next major version is an option.
@TotalWipeOut
CC @weierophinney for visibility, as you raised the original issue.
Not entirely true: "Originally posted by @thexpand at zendframework/zend-i18n#104" (See: #7)
@TotalWipeOut
CC @weierophinney for visibility, as you raised the original issue.
Not entirely true: "Originally posted by @thexpand at zendframework/zend-i18n#104" (See: #7)
Oh yes, thanks for spotting that.
Description
WORK IN PROGRESS
Upon needing message placeholders and seeing the 4 year old issue #7 about needing it, I decided to take a stab at adding message placeholder support into laminas-i18n, as I see it as key missing feature.
(I decided to use the term 'placeholders' instead of 'parameters' so that it is less ambiguous)
~As I really didn't want a BC break, I have hijacked the
$textDomain
parameter of thetranslate
/translatePlural
method to allow passing in placeholders where it seemed most logical going forward. There is a way to be able to provide bothtextDomain
andplaceholders
if that's needed.~Goals
What I've done so far
Translator\TranslatorServiceFactory
to inject a placeholder from configTranslator\Translator
class to add new methods that pass messages through a placeholder compiler before returningExisting tests are all passing
TODO
Before i go any further, I am seeking advice as to if this is the correct approach for adding this without a bc break
The code is not perfect, so any guidance on naming/architecture/design would be amazing. Once i have some feedback, i will make any required changes and get on with the tests
And, a thank you to everyone in the Laminas community for being so kind and generous with your time :heart: