App-I18N

 view release on metacpan or  search on metacpan

README.md  view on Meta::CPAN

    ... do translation here

when you added more message in your application. you might need to update po
messages, but you dont have to delete/recreate these po files, you can just parse your messages again
all of your translations will be kept. eg:

    $ po parse lib

    ... do translation again ...

### Generate locale and mo file for php-gettext or anyother gettext i18n app:

parse strings from `.` path and use --locale (locale directory structure):

    $ cd app
    $ po parse --locale .

this will generate:
    
    po/app.pot

please modify the CHARSET in po/app.pot.

    ... modify CHARSET ...

create new language file (po file and mo file) in locale directory structure:

    $ po lang  --locale en
    $ po lang  --locale zh_TW

this will generate:

    po/en/LC_MESSAGES/app.po
    po/en/LC_MESSAGES/app.mo
    po/zh_TW/LC_MESSAGES/app.po
    po/zh_TW/LC_MESSAGES/app.mo

(you can use --podir option to generate those stuff to other directory)

    ... do translation here ...

if you use mo file , you might need to update mo file.

    $ po update --locale

eg:

    -project (master) % po update --mo --podir locale
        Updating locale/zh_TW/LC_MESSAGES/project.po
        Updating locale/zh_TW/LC_MESSAGES/project.mo
        9 translated messages, 53 untranslated messages.

Note that if you have `po` or `locale` directory exists, then it will be the default po directory.

And `locale` directory will enable `--locale` option.

## Show Translation Status

    $ po status

    Translation Status:
        en_US: [                                                  ]  0% (0/8) 
        zh_TW: [======                                            ] 12% (1/8) 


README.md  view on Meta::CPAN


/api/entry/get/{id}

/api/entry/set/{id}/{msgstr}

/api/entry/insert/{lang}/{msgid}/{msgstr}

### For PHP Developers

If you are using gettext extension for your application,
You should use --locale option to generate locale structure.

And `maketext/l10n.php` file for l10n helper class and functions.


## **TODO**

* Initialize a system-side i18n database:

	po initdb 

TODO.md  view on Meta::CPAN

* merge/update via global po database.
* provide a web service for managing po files.
* hook google translate plugin to web editor textarea.
* parse msgid from database

    $ po parsedb -hlocalhost -uroot -p1234 dbname table_name column_name

* default charset option.

x mo file 
x locale structure

lib/App/I18N.pm  view on Meta::CPAN

        $mofile =~ s{\.po$}{.mo};
        $logger->info( "Generating MO file: $mofile" );
        system(qq{msgfmt -v $translation -o $mofile});
    }
}

sub guess_podir {
    my ($class,$cmd) = @_;
    my $podir;
    $podir = 'po' if -e 'po';
    $podir = 'locale' , $cmd->{locale} = 1 if -e 'locale';
    $podir ||= 'locale' if $cmd->{locale};
    $podir ||= 'po';
    return $podir;
}

sub get_po_path {
    my ( $self, $podir, $lang, $is_locale ) = @_;
    my $pot_name = App::I18N->pot_name;
    my $path;
    if ($is_locale) {
        $path = File::Spec->join( $podir, $lang . ".po" );
    }
    else {
        $path = File::Spec->join( $podir, 'locale', $lang, 'LC_MESSAGES', $pot_name . ".po" );
    }
    return $path;
}

sub update_catalogs {
    my ($self,$podir , $cmd ) = @_;
    my @catalogs = grep !m{(^|/)(?:\.svn|\.git)/}, 
                File::Find::Rule->file
                        ->name('*.po')->in( $podir);

lib/App/I18N.pm  view on Meta::CPAN

    ... do translation here

when you added more message in your application. you might need to update po
messages, but you dont have to delete/recreate these po files, you can just parse your messages again
all of your translations will be kept. eg:

    $ po parse lib

    ... do translation again ...

### Generate locale and mo file for php-gettext or anyother gettext i18n app:

parse strings from `.` path and use --locale (locale directory structure):

    $ cd app
    $ po parse --locale .

this will generate:
    
    po/app.pot

please modify the CHARSET in po/app.pot.

    ... modify CHARSET ...

create new language file (po file and mo file) in locale directory structure:

    $ po lang  --locale en
    $ po lang  --locale zh_TW

this will generate:

    po/en/LC_MESSAGES/app.po
    po/en/LC_MESSAGES/app.mo
    po/zh_TW/LC_MESSAGES/app.po
    po/zh_TW/LC_MESSAGES/app.mo

(you can use --podir option to generate those stuff to other directory)

    ... do translation here ...

if you use mo file , you might need to update mo file.

    $ po update --locale

eg:

    -project (master) % po update --mo --podir locale
        Updating locale/zh_TW/LC_MESSAGES/project.po
        Updating locale/zh_TW/LC_MESSAGES/project.mo
        9 translated messages, 53 untranslated messages.

Note that if you have `po` or `locale` directory exists, then it will be the default po directory.

And `locale` directory will enable `--locale` option.

## Show Translation Status

    $ po status

    Translation Status:
        en_US: [                                                  ]  0% (0/8) 
        zh_TW: [======                                            ] 12% (1/8) 


lib/App/I18N/Command/Auto.pm  view on Meta::CPAN

use REST::Google::Translate;
use base qw(App::I18N::Command);



sub options {
    ( 
        'f|from=s' => 'from',
        't|to=s'   => 'to',
        'backend=s' => 'backend',
        'locale'  => 'locale',
        'verbose' => 'verbose',
        'msgstr' => 'msgstr',   # translate from existing msgstr instead of translating from msgid.
        'overwrite' => 'overwrite',  # overwrite existing msgstr
        'p|prompt'    => 'prompt',
    )
}



sub run {

lib/App/I18N/Command/Auto.pm  view on Meta::CPAN

    unless( $self->{from} ) {
        return $logger->error( "--from [Language] is required. please specify your language to translate." );
    }


    # XXX: check this option
    $self->{backend} ||= 'rest-google';

    my $podir = $self->{podir};
    $podir = App::I18N->guess_podir( $self ) unless $podir;
    $self->{mo} = 1 if $self->{locale};

    mkpath [ $podir ];

    my $pot_name = App::I18N->pot_name;
    my $potfile = File::Spec->catfile( $podir, $pot_name . ".pot") ;
    if( ! -e $potfile ) {
        $logger->info( "$potfile not found." );
        return;
    }

    my $from_lang = $self->{from};
    my $to_lang = $self->{to} || $lang;

    my $pofile;
    if( $self->{locale} ) {
        $pofile = File::Spec->join( $podir , $lang , 'LC_MESSAGES' , $pot_name . ".po" );
    }
    else {
        $pofile = File::Spec->join( $podir , $lang . ".po" );
    }

    my $ext = Locale::Maketext::Extract->new;

    $logger->info( "Reading po file: $pofile" );
    $ext->read_po($pofile);

lib/App/I18N/Command/Auto.pm  view on Meta::CPAN


=head1 DESCRIPTION

auto - auto translate po files.

=head1 OPTIONS

    --from [lang]
    --to [lang]
    --backend [backend]
    --locale
    --prompt 
    --overwrite
    --msgstr
    --verbose

Translate zh_CN.po from en_US to zh_TW

    po auto zh_CN --from en_US --to zh_TW

    po auto zh_CN --from en_US --overwrite --prompt

lib/App/I18N/Command/Gen.pm  view on Meta::CPAN

use App::I18N::Config;
use App::I18N::Logger;
use File::Basename;
use File::Path qw(mkpath);
use File::Find::Rule;
use DateTime;
use base qw(App::I18N::Command);

sub options { (
    'podir=s'  => 'podir',
    'locale'  => 'locale',
    'output=s' => 'output_dir',
    ) }


# XXX: provide template option ?

=pod

Generate inline i18n dictionary dictionary:

lib/App/I18N/Command/Gen.pm  view on Meta::CPAN

END
}

sub run {
    my ( $self, $type ) = @_;

    my $output_dir = $self->{output_dir} || getcwd();
    my $logger = App::I18N->logger();
    my $podir = $self->{podir};
    $podir = App::I18N->guess_podir( $self ) unless $podir;
    $self->{mo} = 1 if $self->{locale};

    use Encode;
    File::Path::mkpath [ $output_dir ];

    if( $self->{locale} ) {


    }
    else {
        my $warnings = $self->_warnings;
        my @pofiles = File::Find::Rule->file->name( "*.po" )->in( $podir );
        for my $pofile ( @pofiles ) {
            my $extract = Locale::Maketext::Extract->new;
            my ($lang) = ($pofile =~ m{(\w+)\.po$} );   # get en_US or zh_TW ... etc
            my $ext = $type;  # "json , js, pl, pm, php";

lib/App/I18N/Command/Gen.pm  view on Meta::CPAN


TYPE:

Can be C<json>, C<js>, C<pm>.

=head1 OPTIONS

    --podir=[path]
            Po files directory

    --locale
            Use locale directory structure.

    --output=[path]
            Path for output.

=cut

lib/App/I18N/Command/Lang.pm  view on Meta::CPAN



=head1 NAME

Lang - create new langauge

=head1 USAGE

    --mo        generate mo file

    --locale    create new po file from pot file in locale directory structure:
                    {podir}/{lang}/LC_MESSAGES/{potname}.po
                this will enable --mo option


    -q
    --quiet     just be quiet

    --podir=[path]
                po directory. potfile will be generated in {podir}/{appname}.pot

=cut

sub options { (
    'q|quiet'  => 'quiet',
    'locale'   => 'locale',
    'mo'       => 'mo',   # generate mo file
    'podir=s'  => 'podir',
    ) }


sub copy_potfile {
    my ( $self, $potfile, $pofile ) = @_;
    use File::Copy;

    $self->logger->info(  "$pofile created.");

lib/App/I18N/Command/Lang.pm  view on Meta::CPAN

        system(qq{msgfmt -v $pofile -o $mofile});
    }
}

sub run {
    my ( $self, @langs ) = @_;
    my $logger = $self->logger();

    my $podir = $self->{podir};
    $podir = App::I18N->guess_podir( $self ) unless $podir;
    $self->{mo} = 1 if $self->{locale};

    mkpath [ $podir ];

    my $pot_name = App::I18N->pot_name;

    my $potfile = File::Spec->catfile( $podir, $pot_name . ".pot") ;
    if( ! -e $potfile ) {
        $logger->info( "$potfile not found." );
        return;
    }

    $logger->info( "$potfile found." );
    my $pofile;
    if( $self->{locale} ) {
        for my $lang ( @langs ) {
            mkpath [ File::Spec->join( $podir , $lang , 'LC_MESSAGES' )  ];
            $pofile = File::Spec->join( $podir , $lang , 'LC_MESSAGES' , $pot_name . ".po" );
            $self->copy_potfile( $potfile , $pofile );
        }
    }
    else {
        for my $lang ( @langs ) {
            $pofile = File::Spec->join( $podir , $lang . ".po" );
            $self->copy_potfile( $potfile , $pofile );

lib/App/I18N/Command/Parse.pm  view on Meta::CPAN


parse - parse i18n string from files.

=head1 USAGE

parse [options] [path]

=head1 OPTIONS

    --lang
    --locale
    --podir=[path]
    --mo

=cut

sub options {
    (
    'q|quiet'  => 'quiet',
    'l|lang=s' => 'language',
    'locale'   => 'locale',   # XXX: use locale directory structure
    'podir=s'  => 'podir',
    'mo'       => 'mo',
    );
}

our $LMExtract = App::I18N->lm_extract();

sub print_help_message {
    # XXX:
    return;

lib/App/I18N/Command/Parse.pm  view on Meta::CPAN

    }

END
}

sub run {
    my ( $self, @args ) = @_;

    my $podir = $self->{podir};
    $podir = App::I18N->guess_podir( $self ) unless $podir;
    $self->{mo} = 1 if $self->{locale};

    my @dirs = @args;
    App::I18N->extract_messages( @dirs );

    # update app.pot catalog
    mkpath [ $podir ];

    my $pot_name = App::I18N->pot_name;
    App::I18N->update_catalog( File::Spec->catfile( $podir, $pot_name . ".pot") );

    if ( $self->{'language'} ) {
        # locale structure
        #    locale/{lang}/LC_MESSAGES/{domain}.po
        #    {podir}/{lang}/LC_MESSAGES/{pot_name}.po
        if( $self->{locale} ) {
            mkpath [ File::Spec->join( $podir, $self->{language}, "LC_MESSAGES" ) ];

            my $pofile = File::Spec->catfile( $podir, $self->{language}, "LC_MESSAGES", $pot_name . ".po" );
            App::I18N->update_catalog( $pofile, $self );
        }
        else {
            App::I18N->update_catalog( File::Spec->catfile( $podir, $self->{language} . ".po" ), $self );
        }
        return;
    }

lib/App/I18N/Command/Server.pm  view on Meta::CPAN


use constant debug => 1;

sub options { (
    'l|lang=s'  => 'language',
    'f|file=s'  => 'pofile',
    'dir=s@'    => 'directories',
    'podir=s'   => 'podir',
    'mo'        => 'mo',
    'verbose'   => 'verbose',
    'locale'    => 'locale',
) }

#  %podata
# 
#     { 
#         [lang_code] => { 
#             name => 'Language Name',
#             path => 'po file path',
#         },
#         ...
#     }
# 


sub run {
    my ($self) = @_;


    $self->{mo} = 1 if $self->{locale};
    my $podir = $self->{podir};
    $podir = App::I18N->guess_podir( $self ) unless $podir;

    my @dirs = @{ $self->{directories} || []  };
    my $logger = App::I18N->logger;

    # pre-process messages
    my $lme = App::I18N->lm_extract;
    if( @dirs ) {
        App::I18N->extract_messages( @dirs );

lib/App/I18N/Command/Server.pm  view on Meta::CPAN


    $logger->info("Importing messages to sqlite memory database.");

    my @pofiles = ( $self->{pofile} ) || File::Find::Rule->file()->name("*.po")->in( $podir );
    my %podata = ();
    for my $file ( @pofiles ) {

        my $langname;
        my $code;

        if( $self->{locale} ) {
            ($langname)  = ( $file =~ m{/([a-zA-Z-_]+)/LC_MESSAGES} );
            ($code) = ( $langname =~ m{^([a-zA-Z]+)} );
        }
        else {
            ($langname)  = ( $file =~ m{([a-zA-Z-_]+)\.po$} );
            ($code) = ( $langname =~ m{^([a-zA-Z]+)} );
        }

        $logger->info( "Importing $langname: $file" );
        $db->import_po( $langname , $file );

lib/App/I18N/Command/Server.pm  view on Meta::CPAN

            : File::ShareDir::dist_dir( "App-I18N" );

    $logger->info("share root: $shareroot");
    $logger->info("podir: $podir") if $podir;
    $logger->info("pofile: @{[ $self->{pofile} ]}") if $self->{pofile};
    $logger->info("language: @{[ $self->{language} ]}") if $self->{language};

    $app->options({
        podir     => $podir,
        shareroot => $shareroot,
        map { $_ => $self->{$_} } grep { $self->{$_} } qw(language pofile locale),
    });
    $app->podata( \%podata );
    $app->db( $db );

    $app->template_path( $shareroot . "/templates" );
    $app->static_path( $shareroot . "/static" );




lib/App/I18N/Command/Status.pm  view on Meta::CPAN

    print " " x ($rest_width);
    print "]";
}

sub run {
    my $self = shift;
    my $logger = $self->logger();

    my $podir = $self->{podir};
    $podir = App::I18N->guess_podir( $self ) unless $podir;
    $self->{mo} = 1 if $self->{locale};

    print "Translation Status:\n";

    my @pofiles = File::Find::Rule->file->name( "*.po" )->in( $podir );
    for my $pofile ( @pofiles ) {
        my $extract = Locale::Maketext::Extract->new;

        my $lang;
        if( $self->{locale} ) {
            ($lang) = ($pofile =~ m{(\w+)/LC_MESSAGES/} );   # get en_US or zh_TW ... etc
        } else {
            ($lang) = ($pofile =~ m{(\w+)\.po$} );   # get en_US or zh_TW ... etc
        }

        $extract->read_po($pofile);

        my $lexicon = $extract->lexicon;

        my $total = scalar keys %$lexicon;

lib/App/I18N/Command/Update.pm  view on Meta::CPAN

use App::I18N::Config;
use App::I18N::Logger;
use File::Basename;
use File::Path qw(mkpath);
use File::Find::Rule;
use base qw(App::I18N::Command);

sub options { (
    'podir=s'  => 'podir',
    'mo'       => 'mo',   # generate mo file
    'locale'   => 'locale',
    ) }

sub run {
    my ( $self, $lang ) = @_;
    my $logger = App::I18N->logger();
    my $podir = $self->{podir};
    $podir = App::I18N->guess_podir( $self ) unless $podir;
    $self->{mo} = 1 if $self->{locale};

    my @pofiles = File::Find::Rule->file->name( "*.po" )->in( $podir );
    for my $pofile ( @pofiles ) {
        $logger->info( "Updating $pofile" );
        if( $self->{mo} ) {
            my $mofile = $pofile;
            $mofile =~ s{\.po$}{.mo};
            $logger->info( "Updating $mofile" );
            qx{msgfmt -v $pofile -o $mofile};
        }

lib/App/I18N/Command/Update.pm  view on Meta::CPAN

__END__

=head1 NAME

App::I18N::Command::Update - update mo files

=head1 USAGE

if you use mo file , you might need to update mo file.

    $ po update --locale

eg:

    -project (master) % po update --mo --podir locale
        Updating locale/zh_TW/LC_MESSAGES/project.po
        Updating locale/zh_TW/LC_MESSAGES/project.mo
        9 translated messages, 53 untranslated messages.
=cut

maketext/l10n.php  view on Meta::CPAN

    }
    return $msg;
}


// helper l10n class

class L10N {
    protected $cur_lang = array();
    protected $a_langs = array();
    protected $localedir;
    protected $domain;
    protected $default_lang;

    function deflang( $lang )
    {
        $this->default_lang = $lang;
        return $this;
    }

    function init( $force_lang = null  )
    {
        $lang = null;
        if( $force_lang ) {
            $lang = $force_lang;
        } else if( isset($_GET['locale']) ) { 
            $lang = $_GET['locale'];
        } else if( isset($_REQUEST['locale']) ) {
            $lang = $_REQUEST['locale'];
        } else if( isset( $_COOKIE['locale'] ) ) {
            $lang = $_COOKIE['locale'];
        }

        if( $lang == null ) {
            $lang = $this->default_lang;
        }
        $this->speak( $lang );
        return $this;
    }


    // set current language
    function speak( $lang )
    {
        $this->cur_lang = $lang;
        setcookie("locale", $lang , time() + 60 * 60 * 24 * 30 );

        $_REQUEST['locale'] = $lang;
        $_COOKIE['locale'] = $lang;

        $this->gettext();
        return $this;
    }

    function is_speaking( $lang )
    {
        return $this->cur_lang == $lang;
    }

maketext/l10n.php  view on Meta::CPAN

    {
        return @$this->a_langs[ $lang ];
    }

    function domain( $domain )
    {
        $this->domain = $domain;
        return $this;
    }

    function localedir( $dir )
    {
        $this->localedir = $dir;
        return $this;
    }

    function gettext( $textdomain = null , $localedir = null )
    {
        if( $textdomain == null )
            $textdomain = $this->domain;

        if( $localedir == null )
            $localedir = $this->localedir;
        if( $localedir == null ) 
            $localedir = dirname(__FILE__) . '/' . 'locale';

        $lang = $this->cur_lang;

        putenv("LANG=$lang");
        setlocale(LC_MESSAGES, $lang );

        bindtextdomain( $textdomain, $localedir );
        bind_textdomain_codeset( $textdomain, 'UTF-8');
        textdomain( $textdomain );
        return $this;
    }
}

function l10n()
{
    global $l10n;
    if( $l10n == null ) {



( run in 3.453 seconds using v1.01-cache-2.11-cpan-ceb78f64989 )