clinton has asked for the wisdom of the Perl Monks concerning the following question:
I'm implementing an i18n framework for my webapp, and trying to make it as transparent as possible: you don't need to know that a variable contains a string that will be magically translated into your chosen language. It just works, eg:
my @days = _('Mon'),_('Tues'),_('Wed'),_('Thurs'),_('Fri'),_('S +at'),_('Sun'); sub day_of_week { my $self = shift; return $days[ $self->{day_num} ]; }
The idea is that any string marked with _( ) gets translated automatically. But there are two "phases": compilation and runtime. At runtime, I map *::_ to a sub which does the translation, but at compile time, I map *::_ to a sub which blesses the string into a simple overloaded class, that looks like this:
package i18n::String; use strict; use warnings; no warnings 'once'; use overload q{""} => sub { $i18n::Current_Lang->maketext( ${ $_[0] } ) }; sub new { my $class = shift; my $string = shift; return bless( \$string, $class ); } 1;
This class delays the translation of the string until the moment that the string needs to be used / stringified, at which point the overloaded stringification calls maketext on the original string.
The problem:
For the most part, this is transparent. I don't need to care whether variables contain strings-to-be-translated or just ordinary strings. The one place where this falls down is ref.
I know that we SHOULD care about the actual return value of ref, but how often do we just leave that out of our code:
sub good { my %params = ref $_[0] eq 'HASH? ? %{shift @_} : @_; } sub bad_but_typical { my %params = ref $_[0] ? %{shift @_} : @_; }
The only thing I could come up with to make this work is this:
{ no strict 'refs'; my $zero_isa = '0::ISA'; # Use a symbolic reference to + make package '0' *$zero_isa = ['i18n::String']; # inherit from i18n::String } package i18n::String; use strict; use warnings; no warnings 'once'; use overload q{""} => sub { $i18n::Current_Lang->maketext( ${ $_[0] } ) }; sub new { my $class = shift; my $string = shift; return bless( \$string,'0' ); # bless the scalar ref into +class '0' instead of $class } 1;
That works, because calling ref $string_object returns zero, which is false. But it's one of those things you hope never to see in production code. And of course, once you've used the "0" class for one object type, you can't use it for any others (unless you were to store the original class name as a property of the object, and let class '0' redispatch it to the correct class.)
So is the answer to this just "teach your fellow developers to program more defensively", or is there another way around it?
|
|---|