Yii and PHP Gettext - yii

I want to make my yii app multilanguage. To do this I want to use gettext (because its much simpler than yii messages).
To this I used this yii extension, I configured the PO files, I made the translations, etc.
The big problem: nothing happened. Nothing was translated.

I can advice you this awesome multilanguage extension!
http://www.yiiframework.com/extension/tstranslation/

To use gettext without any extension follow those steps. In config/main.php set yout target language like this:
'language' = 'ru',
Set messages component to use CGettextMessageSource:
'messages' => array(
'class' => 'CGettextMessageSource',
),
Create messages.po file in protected/messages/ru folder (note: folder name is same as language code). If poedit is used messages.po file must have appropriate headers. Example:
msgid ""
msgstr ""
"Project-Id-Version: FOO BAR 1.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2011-11-11 11:11+0300\n"
"PO-Revision-Date: \n"
"Language: ru\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
"X-Poedit-SourceCharset: utf-8\n"
"X-Poedit-Basepath: .\n"
"X-Poedit-KeywordsList: _ngettext:1,2;t:1c,2\n"
"X-Poedit-SearchPath-0: ../..\n"
Note t:1c,2. This means, that first parameter from function Yii::app() will be used as context (see msgctx) and second as actual string to translate. Without this your i18n will not work!
Now just open messages.po in poedit → Update → do translations → Save.
And messages.mo file will be created and used by Yii.
For you language plurals string see gettext help.

Related

Perl : Scrape website and how to download PDF files from the website using Perl Selenium:Chrome

So I'm studying Scraping website using Selenium:Chrome on Perl, I just wondering how can I download all pdf files from year 2017 to 2021 and store it into a folder from this website https://www.fda.gov/drugs/warning-letters-and-notice-violation-letters-pharmaceutical-companies/untitled-letters-2021 . So far this is what I've done
use strict;
use warnings;
use Time::Piece;
use POSIX qw(strftime);
use Selenium::Chrome;
use File::Slurp;
use File::Copy qw(copy);
use File::Path;
use File::Path qw(make_path remove_tree);
use LWP::Simple;
my $collection_name = "mre_zen_test3";
make_path("$collection_name");
#DECLARE SELENIUM DRIVER
my $driver = Selenium::Chrome->new;
#NAVIGATE TO SITE
print "trying to get toc_url\n";
$driver->navigate('https://www.fda.gov/drugs/warning-letters-and-notice-violation-letters-pharmaceutical-companies/untitled-letters-2021');
sleep(8);
#GET PAGE SOURCE
my $toc_content = $driver->get_page_source();
$toc_content =~ s/[^\x00-\x7f]//g;
write_file("toc.html", $toc_content);
print "writing toc.html\n";
sleep(5);
$toc_content = read_file("toc.html");
This script only download the entire content of the website. Hope someone here can help me and teach me. Thank you very much.
Here is some working code, to help you get going hopefully
use warnings;
use strict;
use feature 'say';
use Path::Tiny; # only convenience
use Selenium::Chrome;
my $base_url = q(https://www.fda.gov/drugs/)
. q(warning-letters-and-notice-violation-letters-pharmaceutical-companies/);
my $show = 1; # to see navigation. set to false for headless operation
# A little demo of how to set some browser options
my %chrome_capab = do {
my #cfg = ($show)
? ('window-position=960,10', 'window-size=950,1180')
: 'headless';
'extra_capabilities' => { 'goog:chromeOptions' => { args => [ #cfg ] } }
};
my $drv = Selenium::Chrome->new( %chrome_capab );
my #years = 2017..2021;
foreach my $year (#years) {
my $url = $base_url . "untitled-letters-$year";
$drv->get($url);
say "\nPage title: ", $drv->get_title;
sleep 1 if $show;
my $elem = $drv->find_element(
q{//li[contains(text(), 'PDF')]/a[contains(text(), 'Untitled Letter')]}
);
sleep 1 if $show;
# Downloading the file is surprisingly not simple with Selenium (see text)
# But as we found the link we can get its url and then use Selenium-provided
# user-agent (it's LWP::UserAgent)
my $href = $elem->get_attribute('href');
say "pdf's url: $href";
my $response = $drv->ua->get($href);
die $response->status_line if not $response->is_success;
say "Downloading 'Content-Type': ", $response->header('Content-Type');
my $filename = "download_$year.pdf";
say "Save as $filename";
path($filename)->spew( $response->decoded_content );
}
This takes shortcuts, switches approaches, and sidesteps some issues (which one need resolve for a fuller utility of this useful tool). It downloads one pdf from each page; to download all we need to change the XPath expression used to locate them
my #hrefs =
map { $_->get_attribute('href') }
$drv->find_elements(
# There's no ends-with(...) in XPath 1.0 (nor matches() with regex)
q{//li[contains(text(), '(PDF)')]}
. q{/a[starts-with(#href, '/media/') and contains(#href, '/download')]}
);
Now loop over the links, forming filenames more carefully, and download each like in the program above. I can fill the gaps further if there's need for that.
The code puts the pdf files on disk, in its working directory. Please review that before running this so to make sure that nothing gets overwritten!
See Selenium::Remove::Driver for starters.
Note: there is no need for Selenium for this particular task; it's all straight-up HTTP requests, no JavaScript. So LWP::UserAgent or Mojo would do it just fine. But I take it that you want to learn how to use Selenium, since it often is needed and is useful.

Perl web server: How to route

As seen in my code below, I am using apache to serve my Perl web server. I need Perl to have multple routes for my client as seen in my %dispatch. If I figure one out I'm sure the rest will be very similar. If we look at my Subroutine sub resp_index, how can I modify this to link to my index.html file located in my root: /var/www/perl directory?
/var/www/perl/perlServer.pl:
#!/usr/bin/perl
{
package MyWebServer;
use HTTP::Server::Simple::CGI;
use base qw(HTTP::Server::Simple::CGI);
my %dispatch = (
'/index.html' => \&resp_index,
# ...
);
sub handle_request {
my $self = shift;
my $cgi = shift;
my $path = $cgi->path_info();
my $handler = $dispatch{$path};
if (ref($handler) eq "CODE") {
print "HTTP/1.0 200 OK\r\n";
$handler->($cgi);
} else {
print "HTTP/1.0 404 Not found\r\n";
print $cgi->header,
$cgi->start_html('Not found'),
$cgi->h1('Not found'),
$cgi->end_html;
}
}
sub resp_index {
my $cgi = shift; # CGI.pm object
return if !ref $cgi;
my $who = $cgi->param('name');
print $cgi->header,
$cgi->start_html("index"),
$cgi-h1("THIS IS INDEX"),
$cgi->end_html;
}
}
my $pid = MyWebServer->new()->background();
print "Use 'kill $pid' to stop server.\n";
I think what you're asking is how do you serve a file from your web server? Open it and print it, like any other file.
use autodie;
sub resp_index {
my $cgi = shift;
return if !ref $cgi;
print $cgi->header;
open my $fh, "<", "/var/www/perl/index.html";
print <$fh>;
}
Unless this is an exercise, really, really, REALLY don't write your own web framework. It's going to be slow, buggy, and insecure. Consider a small routing framework like Dancer.
For example, mixing documents like index.html and executable code like perlServer.pl in the same directory invites a security hole. Executable code should be isolated in their own directory so they can be given wholly different permissions and stronger protection.
Let's talk about this line...
return if !ref $cgi;
This line is hiding an error. If your functions are passed the wrong argument, or no argument, it will silently return and you (or the person using this) will have no idea why nothing happened. This should be an error...
use Carp;
croak "resp_index() was not given a CGI object" if !ref $cgi;
...but really you should use one of the existing function signature modules such as Method::Signatures.
use Method::Signatures;
func resp_index(CGI $cgi) {
...
}

Rails - How to test that ActionMailer sent a specific attachment?

In my ActionMailer::TestCase test, I'm expecting:
#expected.to = BuyadsproMailer.group_to(campaign.agency.users)
#expected.subject = "You submitted #{offer_log.total} worth of offers for #{offer_log.campaign.name} "
#expected.from = "BuyAds Pro <feedback#buyads.com>"
#expected.body = read_fixture('deliver_to_agency')
#expected.content_type = "multipart/mixed;\r\n boundary=\"something\""
#expected.attachments["#{offer_log.aws_key}.pdf"] = {
:mime_type => 'application/pdf',
:content => fake_pdf.body
}
and stub my mailer to get fake_pdf instead of a real PDF normally fetched from S3 so that I'm sure the bodies of the PDFs match.
However, I get this long error telling me that one email was expected but got a slightly different email:
<...Mime-Version: 1.0\r\nContent-Type: multipart/mixed\r\nContent-Transfer-Encoding: 7bit...> expected but was
<...Mime-Version: 1.0\r\nContent-Type: multipart/mixed;\r\n boundary=\"--==_mimepart_50f06fa9c06e1_118dd3fd552035ae03352b\";\r\n charset=UTF-8\r\nContent-Transfer-Encoding: 7bit...>
I'm not matching the charset or part-boundary of the generated email.
How do I define or stub this aspect of my expected emails?
Here's an example that I copied from my rspec test of a specific attachment, hope that it helps (mail can be creating by calling your mailer method or peeking at the deliveries array after calling .deliver):
mail.attachments.should have(1).attachment
attachment = mail.attachments[0]
attachment.should be_a_kind_of(Mail::Part)
attachment.content_type.should be_start_with('application/ics;')
attachment.filename.should == 'event.ics'
I had something similar where I wanted to check an attached csv's content. I needed something like this because it looks like \r got inserted for newlines:
expect(mail.attachments.first.body.encoded.gsub(/\r/, '')).to(
eq(
<<~CSV
"Foo","Bar"
"1","2"
CSV
)
)

Custom error message processor without monkey patch

I've implemented a custom error message processor for Korean language. In Korean, postpositions take different forms depending on the sound of the preceding noun or pronoun.
For example, when marking a subject, ka (가) is used following a vowel and i (이) is used following a consonant.
Examples (hyphens are to denote morpheme boundaries):
Romanization: sakwa-ka ppalkah-ta.
Gloss: Apple-SUBJECT red-PRESENT.
Translation: Apple is red.
Romanization: phainayphul-i tal-ta.
Gloss: Pineapple-SUBJECT sweet-PRESENT.
Translation: Pineapple is sweet.
Therefore, the standard error message system implemented in ActiveModel::Errors is not adequate for Korean. You should either include the attribute in the message making a lot of duplicates ("A is blank", "B is blank", "C is blank", ...), or avoid postpositions after the attribute which is often difficult or makes awkward sentences.
I monkey patched ActiveModel::Errors and altered generate_message to solve this problem. Following is the code (Gist) which is currently in config/initializers in my Rails app.
# encoding: UTF-8
# Original article: http://dailyupgrade.me/post/6806676778/rubyonrails-full-messages-for-korean
# Modified to support more postpositions and client_side_validations gem.
#
# Add Korean translations like this:
# ko:
# errors:
# format: "%{message}"
# messages:
# blank: "%{attribute}((이)) 입력되지 않았습니다"
# taken: "이미 사용 중인 %{attribute}입니다"
# invalid: "잘못된 %{attribute}입니다"
# too_short: "%{attribute}((이)) 너무 짧습니다"
#
class Korean
POSTPOSITIONS = {"은" => "는", "이" => "가", "을" => "를", "과" => "와", "으로" => "로"}
def self.select_postposition(last_letter, postposition)
return postposition unless last_letter >= "가" && last_letter <= "힣"
final = last_letter.mb_chars.last.decompose[2]
if final.nil?
# 받침 없음
POSTPOSITIONS[postposition] || postposition
elsif final == "ㄹ" && (postposition == "으로" || postposition == "로")
# 'ㄹ 받침 + (으)로'를 위한 특별 규칙
"로"
else
# 받침 있음
POSTPOSITIONS.invert[postposition] || postposition
end
end
end
module ActiveModel
class Errors
old_generate_message = instance_method("generate_message")
define_method("generate_message") do |attribute, type = :invalid, options = {}|
msg = old_generate_message.bind(self).(attribute, type, options)
return msg unless I18n.locale == :ko
msg.gsub(/(?<=(.{1}))\(\((은|는|이|가|을|를|과|와|으로|로)\)\)/) do
Korean.select_postposition $1, $2
end
end
end
end
My first question is whether it is possible to achieve the same goal without monkey patching. I'm new to Rails (and Ruby too) so couldn't come up with a better solution.
The second question is about extracting this code from my Rails app and making it into a separate gem. I'm cutting my teeth on developing gems and recently made my first gem. In what place should I put this code in a gem? config/initializers doesn't seem right.
I'm not good at ruby but the following javascript code do the same thing. I hope it may help.
var hasJongsung = function(word) {
return !!(word && word[word.length -1].charCodeAt()>=0xAC00 && word[word.length-1].charCodeAt()<=0xD7A3 && (word[word.length -1].charCodeAt()-0xAC00)%28);
};
source:http://spectrumdig.blogspot.kr/2012/11/unicode-20.html

How can I automatically minify my webpages?

All of my .html and .php webpages should be minified. That means I would like to get all HTML-comments and more than one whitespace-character stripped.
Does an Apache-module exist for minification?
(Or perhaps another method to automatically add a script directly before the output gets sent to the user?)
(I could add a function like the following, but if an Apache-module or another automatic solution existed, I could not forget to do so.)
<?
function sanitize_output($buffer)
{
$search = array(
'/\>[^\S ]+/s', //strip whitespaces after tags, except space
'/[^\S ]+\</s', //strip whitespaces before tags, except space
'/(\s)+/s' // shorten multiple whitespace sequences
);
$replace = array(
'>',
'<',
'\\1'
);
$buffer = preg_replace($search, $replace, $buffer);
return $buffer;
}
?>
Try mod_pagespeed which may be of some use to you.