Skip to content

Commit

Permalink
Add code.
Browse files Browse the repository at this point in the history
Signed-off-by: Hidehito Nozawa <[email protected]>
  • Loading branch information
suin committed May 16, 2015
0 parents commit cc1a1dc
Show file tree
Hide file tree
Showing 13 changed files with 378 additions and 0 deletions.
11 changes: 11 additions & 0 deletions .bashrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
PROJECT=translation
PS1="\`if [ \$? = 0 ]; then echo \[\e[32m\]${PROJECT}\[\e[0m\]; else echo \[\e[31m\]${PROJECT}\[\e[0m\]; fi\`:\w$ "
cd /vagrant

function php {
sudo docker run -it --rm --net host -v $(pwd):/wd -w /wd shouldbee/php /sbin/my_init --quiet --skip-startup-files --skip-runit -- php "$@"
}

function composer {
sudo docker run -it --rm --net host -v $(pwd):/app composer/composer "$@"
}
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.phpmake
/vendor
/composer.phar
/composer.lock
/metrics
/.vagrant
25 changes: 25 additions & 0 deletions Vagrantfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.box = "shouldbee/ubuntu-14.04-with-docker"
config.vm.box_version = "0.5"

host = RbConfig::CONFIG['host_os']

case host
when /darwin/ # mac
cpus = `sysctl -n hw.ncpu`.to_i
when /linux/
cpus = `nproc`.to_i
else
cpus = 2
end

config.vm.provider "vmware_fusion" do |v|
v.vmx["memsize"] = "512"
v.vmx["numvcpus"] = cpus
end
end
26 changes: 26 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "shouldbee/translation",
"description": "Simple translation library",
"keywords": ["translation"],
"homepage": "https://github.com/shouldbee/php-translation",
"license": "MIT",
"authors": [
{
"name": "Hidehito Nozawa",
"email": "[email protected]",
"homepage": "https://www.facebook.com/suinyeze",
"role": "Developer"
}
],
"require": {
"php": ">=5.5"
},
"require-dev": {
"phpunit/phpunit": "3.7.*"
},
"autoload": {
"psr-0": {
"ShouldBee\\Translation": "src/"
}
}
}
3 changes: 3 additions & 0 deletions phpunit-bootstrap.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<?php
// For composer
require_once 'vendor/autoload.php';
37 changes: 37 additions & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
bootstrap="phpunit-bootstrap.php"
processIsolation="false"
verbose="true"
strict="false"
colors="true">
<testsuites>
<testsuite name="PHPUnit">
<directory>tests/*/*</directory>
</testsuite>
</testsuites>

<logging>
<log
type="coverage-html"
target="coverage"
charset="UTF-8"
yui="true"
highlight="false"
lowUpperBound="35"
highLowerBound="70"/>
<!--<log type="coverage-text" target="php://stdout" lowUpperBound="35" highLowerBound="70" />-->
</logging>

<filter>
<whitelist>
<directory suffix=".php">src</directory>
<!-- <file>/path/to/file</file> -->
<exclude>
<!-- <file>/path/to/file</file> -->
<directory suffix="Interface.php">src</directory>
<directory>tests/*/*</directory>
</exclude>
</whitelist>
</filter>
</phpunit>
97 changes: 97 additions & 0 deletions src/ShouldBee/Translation/Negotiation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php

namespace ShouldBee\Translation;


class Negotiation
{
/**
* @param string[] $availableLanguages
* @return string
*/
public static function negotiate(array $availableLanguages)
{
$language = $availableLanguages[0];
$language = self::negotiateByAcceptLanguage($availableLanguages, $language);
$language = self::negotiateByQueryString($availableLanguages, $language);
$language = self::negotiateByCookie($availableLanguages, $language);

return $language;
}

/**
* @param string $language
* @param string $cookieDomain
*/
public static function remember($language, $cookieDomain = null)
{
setcookie('language', $language, strtotime('+1 year'), '/', $cookieDomain);
}

/**
* @param array $languages
* @param $defaultLanguage
* @return int|string
*/
private static function negotiateByAcceptLanguage(array $languages, $defaultLanguage)
{
if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) === false){
return $defaultLanguage;
}

$acceptLanguages = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);

foreach ($acceptLanguages as $acceptLanguageInfo) {
$acceptLanguage = explode(';', $acceptLanguageInfo)[0];

foreach ($languages as $language) {
if (stripos($language, $acceptLanguage) === 0) {
return $language;
}
}
}

return $defaultLanguage;
}

/**
* @param array $languages
* @param string $defaultLanguage
* @return string
*/
private static function negotiateByQueryString(array $languages, $defaultLanguage)
{
if (isset($_GET['language']) === false) {
return $defaultLanguage;
}

$parameterLanguage = strtolower($_GET['language']);

if (in_array($parameterLanguage, $languages) === false) {
return $defaultLanguage;
}

return $parameterLanguage;
}

/**
* @param array $languages
* @param string $defaultLanguage
* @return string
*/
private static function negotiateByCookie(array $languages, $defaultLanguage)
{
if (isset($_COOKIE['language']) === false) {
return $defaultLanguage;
}

$cookieLanguage = strtolower($_COOKIE['language']);

if (in_array($cookieLanguage, $languages) === false) {
return $defaultLanguage;
}

return $cookieLanguage;
}
}

79 changes: 79 additions & 0 deletions src/ShouldBee/Translation/Translator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php

namespace ShouldBee\Translation;

class Translator
{
/**
* @var string[]
*/
private $catalogues = [];

/**
* @var string[]
*/
private $defaultCatalogue = [];

/**
* @var string
*/
private $language;

/**
* @param string[] $defaultCatalogue
*/
public function __construct(array $defaultCatalogue)
{
$this->defaultCatalogue = $defaultCatalogue;
}

/**
* @param string $language
* @param string[] $catalogue
*/
public function setCatalogue($language, array $catalogue)
{
$this->catalogues[$language] = $catalogue;
}

/**
* @param string $language
*/
public function setLanguage($language)
{
$this->language = $language;
}

/**
* @param string $key
* @param string ... $value
* @return string
*/
public function translate($key, $value = null)
{
if (
array_key_exists($this->language, $this->catalogues)
and array_key_exists($key, $this->catalogues[$this->language])
) {
$message = $this->catalogues[$this->language][$key];
} elseif (array_key_exists($key, $this->defaultCatalogue) === true) {
// default fallback
$message = $this->defaultCatalogue[$key];
} else {
// can't translate
return $key;
}

if (func_num_args() == 1) {
return $message;
}

$params = array_slice(func_get_args(), 1);

foreach ($params as $i => $param) {
$message = str_replace('{' . $i . '}', $param, $message);
}

return $message;
}
}
30 changes: 30 additions & 0 deletions tests/ShouldBee/Translation/NegotiationTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

namespace ShouldBee\Translation;

class NegotiationTest extends \PHPUnit_Framework_TestCase
{
public function testNegotiation()
{
$availableLanguages = ['en', 'ja', 'ko', 'zh'];

// default
$language = Negotiation::negotiate($availableLanguages);
$this->assertSame('en', $language);

// set Accept-Language.
$_SERVER['HTTP_ACCEPT_LANGUAGE'] = "ja-JP,ja;q=0.8,en;q=0.6";
$language = Negotiation::negotiate($availableLanguages);
$this->assertSame('ja', $language);

// set query string.
$_GET['language'] = 'ko';
$language = Negotiation::negotiate($availableLanguages);
$this->assertSame('ko', $language);

// set cookie.
$_COOKIE['language'] = 'zh';
$language = Negotiation::negotiate($availableLanguages);
$this->assertSame('zh', $language);
}
}
47 changes: 47 additions & 0 deletions tests/ShouldBee/Translation/TranslatorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

namespace ShouldBee\Translation;

class TranslatorTest extends \PHPUnit_Framework_TestCase
{
public function testTranslation()
{
$defaultCatalogue = json_decode(file_get_contents(__DIR__ . '/catalogue-default.json'), true);
$englishCatalogue = json_decode(file_get_contents(__DIR__ . '/catalogue-en.json'), true);
$japaneseCatalogue = json_decode(file_get_contents(__DIR__ . '/catalogue-ja.json'), true);

$translator = new Translator($defaultCatalogue);
$translator->setCatalogue('ja', $japaneseCatalogue);
$translator->setCatalogue('en', $englishCatalogue);

// no key to translate
$message = $translator->translate('foo.bar');
$this->assertSame($message, 'foo.bar');

// output default catalogue translation, as no language is specified.
$message = $translator->translate('delete.confirm');
$this->assertSame($message, 'delete ok?');

// set English to current language.
$translator->setLanguage('en');
$message = $translator->translate('user.name');
$this->assertSame($message, 'User name');

// replace placeholders
$message1 = $translator->translate("hello.user", "alice");
$message2 = $translator->translate("files.summary", 12114, "Macintosh HD");
$this->assertSame($message1, 'Hello, alice');
$this->assertSame($message2, 'The disk Macintosh HD contains 12114 file(s).');

// output default catalogue translation, as there is no English translation for the key.
$message = $translator->translate("delete.confirm");
$this->assertSame($message, 'delete ok?');

// set Japanese to current language.
$translator->setLanguage('ja');
$message1 = $translator->translate("user.name");
$message2 = $translator->translate("files.summary", 12114, "Macintosh HD");
$this->assertSame($message1, 'ユーザ名');
$this->assertSame($message2, 'ディスクMacintosh HDに12114個のファイル');
}
}
5 changes: 5 additions & 0 deletions tests/ShouldBee/Translation/catalogue-default.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"hello": "Hello",
"user.name": "User name",
"delete.confirm": "delete ok?"
}
6 changes: 6 additions & 0 deletions tests/ShouldBee/Translation/catalogue-en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"hello": "Hello",
"user.name": "User name",
"hello.user": "Hello, {0}",
"files.summary": "The disk {1} contains {0} file(s)."
}
6 changes: 6 additions & 0 deletions tests/ShouldBee/Translation/catalogue-ja.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"hello": "こんにちは",
"user.name": "ユーザ名",
"hello.user": "こんにちは, {0}さん",
"files.summary": "ディスク{1}に{0}個のファイル"
}

0 comments on commit cc1a1dc

Please sign in to comment.