view release on metacpan or search on metacpan
... 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)
/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
* 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 ) {