lib/MGH_Biostat/Locale/POCatalog.pm


package MGH_Biostat::Locale::POCatalog;
use Modern::Perl '2012';
use experimental 'switch';
use Carp;

use Path::Tiny;
use Set::Tiny 'set';

use MGH_Biostat::Locale::PO;

use constant CTXSEP => chr(4);
our $DEFAULTS = { 'dir' => 'po', 'default_lang' => 'en-US' };

sub new {
    my $class = shift;

    my $self = { %$DEFAULTS, @_ };

    if ( $self->{'dir'} !~ m[/$] ) { $self->{'dir'} .= '/'; }

    my $langs = [];
    for ( path( $self->{'dir'} )->children(qr/\.po$/) ) {
        push @$langs, $_->basename('.po');
    }
    $self->{'langs'} = set($langs);

    my $order = [
        $self->{'default_lang'},
        sort grep { $_ ne $self->{'default_lang'}; } $self->{'langs'}->members(),
    ];
    $self->{'langs_order'} = $order;

    my $data = {};
    foreach my $lang ( $self->{'langs'}->members() ) {
        my $pos = MGH_Biostat::Locale::PO->load_file_asarray( $self->{'dir'} . $lang . '.po', 'utf8' );
        foreach my $po (@$pos) {
            my $key = $po->hashkey;
            if ( !defined( $po->msgid_plural ) and ( !defined( $po->msgstr ) or ( $po->msgstr eq '' ) ) ) {
                no warnings 'uninitialized';
                warn 'Missing translation for [' . $lang . '|' . $po->msgctxt . '|' . $po->msgid . ']; setting to msgid.' . "\n";
                $po->msgstr( $po->msgid );
            }
            $data->{$lang}{$key} = $po;
        }
    }
    $self->{'data'} = $data;

    return bless $self, $class;
}

sub langs {
    my $self = shift;
    return $self->{'langs'};
}

sub default_lang {
    my $self = shift;
    return $self->{'default_lang'};
}

sub langs_order {
    my $self = shift;
    return $self->{'langs_order'};
}

sub is_valid_lang {
    my $self = shift;
    my ($lang) = @_;
    return $self->{'langs'}->member($lang);
}

# look up a translation
# returns a MGH_Biostat::Locale::PO object
# the first parameter *must* be the language
# the second parameter is the lookup string
# if there is a context to be provided, you can either pass a third parameter,
#   or use the CTXSEP constant to create a context lookup string
# if there is any kind of error, a warning will be fired,
#   and a Locale::PO object with the same string as request will be returned
sub lookup {
    my $self = shift;
    my ( $lang, $msgid, $context ) = @_;

    if ( $self->is_valid_lang($lang) ) {
        my $key  = ( defined($context) ? $context . CTXSEP : '' ) . $msgid;
        my $data = $self->{'data'};
        if ( defined( $data->{$lang}{$key} ) ) {
            return $data->{$lang}{$key};
        }
        elsif ( defined( $data->{ $self->{'default_lang'} }{$key} ) ) {
            no warnings 'uninitialized';    # $context may not be defined
            carp "Key [$context|$msgid] not defined for $lang.";
            return $data->{ $self->{'default_lang'} }{$key};
        }
        else {
            no warnings 'uninitialized';    # $context may not be defined
            carp "Key [$context|$msgid] not defined for any lang.";
            return MGH_Biostat::Locale::PO->new( -msgid => $msgid, -msgstr => $msgid, -msgctxt => $context );
        }
    }
    else {
        carp "Language $lang not supported.";
        return MGH_Biostat::Locale::PO->new( -msgid => $msgid, -msgstr => $msgid, -msgctxt => $context );
    }
}

1;