suin.io

gettext的な多言語対応方法での文法数の実装をPHPで考える

suin2010年3月19日

勉強がてら、gettextのngettext()関数を参考に、PHPで文法数(grammatical number)の切り替え機能を書いてみます。関数number()を作り、に整数を入れると、数量にあった形式(0 ~ 3)を導く処理を作ります。形式は主に単数・複数・双数(dual)ですが、拡張性(新たなパターンの言語の対応)を考えて、形式には0~3の抽象的な値を割り振っておきます。例えば、0は単数形・1は複数形の場合、0は単数形・1は双数形・2は複数形の場合などがあります。

  • 0 : 形式 A
  • 1 : 形式 B
  • 2 : 形式 C
  • 3 : 形式 D

言語によって、使い分ける形式の数が異なります。少ない言語で1つの形式を、多い言語で4つの形式を使い分けます。また、形式の数が同じでも、先行する数字によって、形式の使い分け方が言語によって異なります。この点を総括すると、11パターンが考えられます。

  1. パターン1 : 形式が1つ
  2. パターン2.1 : 形式が2つ » 単数形が1に使われる
  3. パターン2.2 : 形式が2つ » 単数形が0と1に使われる
  4. パターン3.1 : 形式が3つ » 0が特別
  5. パターン3.2 : 形式が3つ » 1と2が特別
  6. パターン3.3 : 形式が3つ » /00$//[2-9][0-9]$/が特別
  7. パターン3.4 : 形式が3つ » /1[2-9]$/が特別
  8. パターン3.5 : 形式が3つ » /1$//[2-4]$/が特別 (/1[1-4]$/以外)
  9. パターン3.6 : 形式が3つ » 1と2, 3, 4が特別
  10. パターン3.7 : 形式が3つ » /[2-4]$/が特別
  11. パターン4.1 : 形式が4つ » /0*[2]$//0*[3-4]$/が特別

それでは、以上の順にしたがって、それぞれのパターンの実装を書いていきます。もし、誤りがあれば教えてください m(_ _ )m

パターン1 : 形式が1つ

数の文法カテゴリがなく、ひとつの形式で表す言語です。アジア型の言語に多いです。日本人にとっては一番身近なパターンです。

対象言語
日本語・韓国語・ベトナム語・トルコ語など
<php
function number($int)
{
	return 0;
}
?>

パターン2 : 形式が2つ

数を表すのに2つの形式を使い分ける言語です。ヨーロッパ型の言語の中でも西ヨーロッパに多いです。日本人にとって、このパターンも理解しやすいほうだと思います。

パターン2.1 : 単数形が1に使われる

対象言語
英語・オランダ語・ドイツ語・エストニア語・フィンランド語・ギリシャ語・イタリア語・ポルトガル語・スペイン語・エスペラント語など
<php
function number($int)
{
	if ( $int == 1 )
	{
		return 0; // 1
	}

	return 1;
}
?>

パターン2.2 : 単数形が0と1に使われる

対象言語
フランス語・ブラジルポルトガル語など
<php
function number($int)
{
	if ( $int <= 1 )
	{
		return 0;
	}

	return 1;
}
?>

パターン3 : 形式が3つ

数を表すのに3つの形式を使い分ける言語です。ヨーロッパ型の言語の中でも東ヨーロッパに多いです。これらの言語の中には、単数・複数に加え、双数の概念があります。この言語では、基本的には、双数が2で、複数は3以上です。極端なことを言うと、英語などが「1, いっぱい」と数えるのに対して、「1, 2, いっぱい」という数え方をする言語ということになります。

また、ヨーロッパ型の言語では、1~19まで数えて20以降は数字を組み合わせるといった20進法的な面があり、そのため1~19に関わる数字が不規則になることがあるようです。

パターン3.1 : 0が特別

対象言語
ラトビア語
<php
function number($int)
{
	// 1, 21, 31, 41, 51, 61, 71, 81, 91, 
	// 101, 121, 131, 141, 151, 161, 171, 181, 191, 
	// 201, 221, 231, 241, 251, ...
	if ( $int % 10 == 1 and $int % 100 != 11 )
	{
		return 0;
	}
	// 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 
	// 12, 13, 14, 15, 16, 17, 18, 19, 20, 
	// 22, 23, 24, 25, 26, 27, 28, 29, 30, ...
	elseif ( $int != 0 )
	{
		return 1;
	}

	// 0 [特別]
	return 2;
}
?>

パターン3.2 : 1と2が特別

対象言語
アイルランド語
<?php
function number($int)
{
	// 1 [特別]
	if ( $int == 1 )
	{
		return 0;
	}
	// 2 [特別]
	elseif ( $int == 2 )
	{
		return 1;
	}

	//  0, 3, 4, 5, 6, 7, 8, 9, 
	// 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, ...
	return 2;
}
?>

パターン3.3 : /00$/と/[2-9][0-9]$/が特別

対象言語
ルーマニア語

つまり、ルーマニア語では、2から19までは形式1になります。

<?php 
function number($int)
{
	// 1
	if ( $int == 1 )
	{
		return 0;
	}
	// 0, 2, 3, 4, 5, 6, 7, 8, 9, 
	// 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 
	// 101, 102, 103, 104, 105, 106, 107, 108, 109, 
	// 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, ...
	elseif ( $int == 0 or ( $int % 100 > 0 and $int % 100 < 20 ) )
	{
		return 1;
	}

	// 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,  [特別]
	// 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, ...
	// 100, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, ...
	return 2;
}
?>

パターン3.4 : /1[2-9]$/が特別

対象言語
リトアニア語

うーん、これであってるか心配。

<?php
function number($int)
{
	// 1, 21, 31, 41, 51, 61, 71, 81, 91, 101, 121, ...
	if ( $int % 10 == 1 and $int % 100 != 11 )
	{
		return 0;
	}
	// 2, 3, 4, 5, 6, 7, 8, 9, 22, 23, 24, 25, 26, 27, 28, 29, 32, 33, ...
	elseif ( $int % 10 >= 2 and ( $int % 100 < 10 or $int % 100 >= 20 ) )
	{
		return 1;
	}

	// 0, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, [特別]
	// 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 110,
	// 111, 112, 113, 114, 115, 116, 117, 118, 119 120, ...
	return 2;
}
?>

パターン3.5 : /1$/と/[2-4]$/が特別 (/1[1-4]$/以外)

対象言語
クロアチア語・セルビア語・ロシア語・ウクライナ語
<?php
function number($int)
{
	// 1, 21, 31, 41, 51, 61, 71, 81, 91,  [特別]
	// 101, 121, 131, 141, 151, 161, 171, 181, 191, 
	// 201, 221, 231, 241, 251, ...
	if ( $int %10 == 1 and $int % 100 != 11 )
	{
		return 0;
	}
	// 2, 3, 4, 22, 23, 24, 32, 33, 34, 42, 43, 44,  [特別]
	// 52, 53, 54, 62, 63, 64, 72, 73, 74, 82, 83, 84, 
	// 92, 93, 94, 102, 103, 104, 122, 123, 124, ...
	elseif ( $int % 10 >= 2 and $int % 10 <= 4 and ( $int % 100 < 10 or $int % 100 >= 20 ) )
	{
		return 1;
	}

	// 0, 5, 6, 7, 8, 9, 10, 
	// 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 
	// 25, 26, 27, 28, 29, 30, 35, 36, 37, 38, 39, ...
	return 2;
}
?>

パターン3.6 : 1と2, 3, 4が特別

対象言語
スロバキア語・チェコ語
<?php
function number($int)
{
	// 1 [特別]
	if ( $int == 1 )
	{
		return 0;
	}
	// 2, 3, 4 [特別]
	elseif ( $int >= 2 and $int <= 4 )
	{
		return 1;
	}

	// 0, 5, 6, 7, 8, 9, 
	// 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 
	// 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, ...
	return 2;
}
?>

パターン3.7 : /[2-4]$/が特別

対象言語
ポーランド語
<?php
function number($int)
{
	// 1
	if ( $int == 1 )
	{
		return 0;
	}
	// 2, 3, 4, 22, 23, 24, 32, 33, 34,  [特別]
	// 42, 43, 44, 52, 53, 54, 62, 63, 64, 
	// 72, 73, 74, 82, 83, 84, 92, 93, 94, ...
	elseif ( $int % 10 >= 2 and $int % 10 <= 4 and ( $int % 100 < 10 or $int % 100 >= 20 ) )
	{
		return 1;
	}

	// 0, 5, 6, 7, 8, 9, 10, 
	// 11, 12, 13, 14, 15, 16, 17, 18, 19, 
	// 20, 21, 25, 26, 27, 28, 29, 
	// 30, 31, 35, 36, 37, 38, 39, ...
	return 2;
}
?>

パターン4 : 形式が4つ

数を表すのに4つの形式を使い分ける言語です。

パターン4 .1 : /0*[2]$/と/0*[3-4]$/が特別

対象言語
スロベニア語
<?php
function number($int)
{
	// 1, 101, 201, ...
	if ( $int % 100 == 1 )
	{
		return 0;
	}
	// 2, 102, 202, ... [特別]
	elseif ( $int % 100 == 2 )
	{
		return 1;

	}
	// 3, 4, 103, 104, 203, 204, ... [特別]
	elseif ( $int % 100 == 3 or $int % 100 == 4 )
	{
		return 2;
	}

	// 0, 5, 6, 7, 8, 9, 
	// 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 
	// 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, ...
	return 3;
}
?>
RELATED POSTS