web-dev-qa-db-ja.com

ベストプラクティス多言語Webサイト

私はこの数ヶ月間ずっとこの質問に苦労してきました、しかし私は前にすべての可能なオプションを調査する必要がある状況にいませんでした。今、私はそれが可能性を知り、私の今後のプロジェクトで使う私自身の個人的な好みを作り出す時が来たように感じます。

最初に探している状況をスケッチしましょう

私は長い間使用してきたコンテンツ管理システムをアップグレード/再開発しようとしています。しかし、私は多言語がこのシステムの大きな改善であると感じています。以前はフレームワークを使用していませんでしたが、次のプロジェクトにはLaraval 4を使用します。 Laravelは、PHPをコード化するためのよりクリーンな方法の最良の選択と思われます。 Sidenote: Laraval4 should be no factor in your answer。私はプラットフォーム/フレームワークに依存しない一般的な翻訳方法を探しています。

何を翻訳すべきか

私が探しているシステムはできるだけユーザーフレンドリーである必要があるので翻訳を管理する方法はCMSの中にあるべきです。翻訳ファイルやhtml/phpで解析されたテンプレートを修正するためにFTP接続を開始する必要はないはずです。

さらに、私はおそらく追加のテーブルを作成する必要なしに複数のデータベーステーブルを翻訳する最も簡単な方法を探しています。

私は自分で何を考え出しましたか

私は自分自身で物事を探し、読んで、試しています。私が持っているオプションがいくつかあります。しかし、私が実際に求めているもののベストプラクティス方法に到達したとはまだ感じません。今のところ、これが私が思い付いたことですが、この方法にも副作用があります。

  1. PHP解析テンプレート:テンプレートシステムはPHPによって解析されるべきです。これにより、テンプレートを開いて変更することなく、翻訳されたパラメータをHTMLに挿入できます。それに加えて、PHP解析されたテンプレートは私に完全なウェブサイトのための1つのテンプレートを持つ能力を与えます(私が以前持っていた)各言語のためのサブフォルダーを持つ代わりに。この目標を達成する方法は、Smarty、TemplatePower、Laravel's Blade、その他のテンプレートパーサーのいずれかです。私が言ったようにこれは書かれた解決策から独立しているべきです。
  2. データベース駆動型:おそらくこれについてもう言及する必要はないでしょう。しかし、解決策はデータベース主導でなければなりません。 CMSはオブジェクト指向とMVCを目的としているので、文字列の論理データ構造を考える必要があります。私のテンプレートは構造化されているので:templates/Controller/View.phpおそらくこの構造は最も理にかなっているでしょう:Controller.View.parameter。データベーステーブルには、これらのフィールドとvalueフィールドがあります。テンプレートの中ではecho __('Controller.View.welcome', array('name', 'Joshua'))のようなソート方法を使うことができ、パラメータはWelcome, :nameを含みます。したがって、結果はWelcome, Joshuaです。 :nameのようなパラメータはエディタによって理解しやすいので、これはこれを行うための良い方法のようです。
  3. データベースの負荷が低い:もちろん、これらの文字列が移動中にロードされている場合、上記のシステムでデータベースの負荷が増えます。したがって、言語ファイルが管理環境で編集/保存されたらすぐにその言語ファイルを再レンダリングするキャッシングシステムが必要です。ファイルが生成されるため、優れたファイルシステムレイアウトも必要です。 languages/en_EN/Controller/View.phpや.iniを使うことができると思います。おそらく、.iniはさらに早く構文解析されます。これはformat parameter=value;のデータを含むべきです。レンダリングされる各ビューには、存在する場合は独自の言語ファイルを含めることができるため、これがこれを実行する最善の方法であると思います。言語パラメータは、パラメータが互いに上書きされないように、グローバルスコープではなく特定のビューにロードする必要があります。
  4. データベーステーブルの翻訳:これは実は私が最も心配していることです。私はNews/Pages/etcの翻訳を作成する方法を探しています。できるだけ早く。モジュールごとに2つのテーブルを持つこと(たとえばNewsNews_translations)はオプションですが、良いシステムを得るために多くの作業をしたいと思います。私が思いついたものの1つは、私が書いたdata versioningシステムに基づいています。データベーステーブル名Translationsが1つあり、このテーブルにはlanguagetablenameおよびprimarykeyの固有の組み合わせがあります。例えば、en_En/News/1(ID = 1のニュース項目の英語版を参照)。しかし、この方法には2つの大きな欠点があります。1つ目はデータベース内の大量のデータでかなり長くなる傾向があり、2つ目はこの設定を使用してテーブルを検索するのが面倒なことです。例えば。アイテムのSEOナメクジを検索するのは全文検索になるでしょう。しかし、その一方で、すべてのテーブルに翻訳可能なコンテンツを非常に高速に作成するための迅速な方法ですが、このプロが欠点を克服するとは考えていません。
  5. フロントエンドの作業:フロントエンドについてもいくつかの考え方が必要になります。もちろん、利用可能な言語をデータベースに保存し、必要な言語を(非)アクティブにします。このようにして、スクリプトは言語を選択するためのドロップダウンを生成することができ、バックエンドはCMSを使ってどんな翻訳ができるかを自動的に決めることができます。選択された言語(例えばen_EN)は、それから、ビューのための言語ファイルを得るとき、またはウェブサイト上のコンテンツアイテムのための正しい翻訳を得るために使用されるであろう。

だから、そこにあります。私の考えはこれまでのところ。日付などのローカライズオプションもまだ含まれていませんが、私のサーバーはPHP5.3.2以降をサポートしているので、ここで説明するようにintl拡張子を使用するのが最善の選択肢です。 http://devzone.zend.com/1500/internationalization-in-php-53 / - しかし、これは後の開発スタジアムで役に立ちます。今のところ主な問題はウェブサイトの内容の翻訳のベストプラクティスを持つ方法です。

私がここで説明したすべてのものの他に、私はまだ私がまだ決めていない別のことをまだ持っています、それは簡単な質問のように見えます、しかし実際にそれは私に頭痛を与えています:

URLの翻訳これをするべきかどうか?そしてどのように?

だから、私がこのURLを持っているなら:http://www.domain.com/about-usそして英語が私のデフォルトの言語です。自分の言語にオランダ語を選択した場合、このURLをhttp://www.domain.com/over-onsに翻訳する必要がありますか?あるいは、簡単な道を進んで、/aboutに表示されているページの内容を変更するだけでよいのです。最後のものは同じURLの複数のバージョンを生成するので、有効なオプションではないように見えます。これはコンテンツのインデックス作成が正しく行われないためです。

他の選択肢は代わりにhttp://www.domain.com/nl/about-usを使うことです。これにより、コンテンツごとに少なくとも一意のURLが生成されます。また、http://www.domain.com/en/about-usなどの別の言語に移動する方が簡単であり、提供されるURLはGoogleとHumanの両方のユーザーにとって理解しやすいものです。このオプションを使用して、デフォルトの言語で何をしますか?デフォルトの言語で、デフォルトで選択されている言語を削除する必要がありますか?それでhttp://www.domain.com/en/about-ushttp://www.domain.com/about-usにリダイレクトします...私の目にはこれが最善の解決策です、なぜならCMSが1つの言語だけのためにセットアップされるときURLにこの言語識別を持つ必要がないからです。

そして3番目のオプションは両方のオプションからの組み合わせです:主言語に "language-identification-less" -URL(http://www.domain.com/about-us)を使うこと。そして、サブ言語用に翻訳されたSEOスラッグを含むURLを使用してください:http://www.domain.com/nl/over-onshttp://www.domain.com/de/uber-uns

私の質問があなたの頭が割れるのを願っている、彼らは確かに私のものを割った!それは私がすでにここで質問として物事を解決するのを助けました。私が以前に使用した方法と私が今後のCMSのために持っているという考えを見直す可能性を私に与えてください。

私はこのテキストの束を読むために時間を割いてくれてありがとうございます!

// Edit #1

私は言及するのを忘れていました:__()関数は与えられた文字列を翻訳するための別名です。このメソッドの中には、明らかに利用可能な翻訳がまだないときにデフォルトのテキストがロードされるある種の代替メソッドがあるはずです。翻訳が見つからない場合は、挿入するか、翻訳ファイルを再生成する必要があります。

163
Joshua - Pendo

トピックの前提

多言語サイトには、3つの異なる側面があります。

  • インターフェース翻訳
  • コンテンツ
  • uRLルーティング

それらはすべて異なる方法で相互接続されていますが、CMSの観点からは、異なるUI要素を使用して管理され、異なる方法で保存されています。あなたは自分の実装と最初の2つの理解に自信があるようです。質問は後者の側面に関するものでした - 「URLの翻訳、これを行うべきかどうか、そしてどのようにしますか?」

URLは何でできていますか?

非常に重要なことは、 IDN に夢中にならないことです。代わりに 音訳 (また転写とローマ字変換)を支持してください。一見したところIDNは国際的なURLのための実行可能なオプションであるように見えますが、それは実際に2つの理由で宣伝されているように機能しません:

  • 'ч''ž'のような非ASCII文字を'%D1%87''%C5%BE'に変換するブラウザもあります。
  • ユーザーがカスタムテーマを持っている場合、テーマのフォントはそれらの文字のシンボルを持っていない可能性が非常に高いです。

私は実際にYiiベースのプロジェクト(恐ろしいフレームワーク、IMHO)で数年前にIDNアプローチを試みました。私はその解決策を廃止する前に、上記の問題の両方に遭遇しました。また、これが攻撃の原因になる可能性があります。

利用可能なオプション...私がそれらを見るように。

基本的にあなたは2つの選択肢を持っています。

  • http://site.tld/[:query][:query]は言語とコンテンツの両方の選択を決定します

  • http://site.tld/[:language]/[:query]:URLの[:language]部分は言語の選択を定義し、[:query]はコンテンツを識別するためにのみ使用されます

クエリはΑとΩです。

http://site.tld/[:query]を選ぶとしましょう。

その場合、[:query]セグメントのコンテンツという1つの主要な言語ソースがあります。そして2つの追加情報源:

  • その特定のブラウザの値$_COOKIE['lang']
  • hTTP Accept-Language内の言語のリスト (1)(2) ヘッダ

まず、クエリを定義済みのルーティングパターンのいずれかに一致させる必要があります(選択がLaravelの場合は、 ここを読んでください )。パターンのマッチングが成功したら、その言語を見つける必要があります。

あなたはパターンのすべてのセグメントを通過する必要があります。それらすべてのセグメントの潜在的な翻訳を見つけ、どの言語が使用されていたかを判断します。 2つの追加のソース(cookieとヘッダー)は、( "if"ではなく)ルーティング競合を解決するために使用されます。

例えばhttp://site.tld/blog/novinkaを取ります。

これは"блог, новинка"の音訳です。英語ではおよそ"blog", "latest"を意味します。

あなたがすでに気づくことができるように、ロシア語で「блог」は「ブログ」として音訳されます。つまり、[:query]の最初の部分では、(ベストケースのシナリオでは)、可能な言語の['en', 'ru']リストが表示されます。それから、あなたは次のセグメント - "novinka"を取ります。それは可能性のリストに1つの言語しかないかもしれません:['ru']

リストに1つの項目があれば、言語は正常に見つかりました。

しかし、場合によっては2(例:ロシア語とウクライナ語)以上の可能性がある場合、または0の可能性がある場合もあります。正しいオプションを見つけるには、クッキーやヘッダーを使用する必要があります。

そして、それ以外のすべてが失敗した場合は、サイトのデフォルトの言語を選択します。

パラメータとしての言語

代わりにURLを使用することもできます。これはhttp://site.tld/[:language]/[:query]として定義できます。この場合、クエリを翻訳するときに、言語を推測する必要はありません。その時点で、どちらを使用すればよいかがすでにわかっているからです。

言語の二次的な情報源もあります:クッキーの値です。しかし、「コールドスタート」(ユーザーが初めてカスタムクエリでサイトを開いたとき)の場合、未知の数の可能な言語を扱っていないため、Accept-Languageヘッダーを使用しても意味がありません。

代わりに、3つの単純で優先順位の付けられたオプションがあります。

  1. [:language]セグメントが設定されている場合はそれを使用
  2. $_COOKIE['lang']が設定されている場合はそれを使用
  3. デフォルトの言語を使用

あなたがその言語を持っているとき、あなたは単に質問を翻訳しようとします、そして、翻訳が失敗したならば、(ルーティング結果に基づいて)その特定のセグメントのために「デフォルト値」を使います。

ここに第三の選択肢ではないですか?

はい、技術的には両方のアプローチを組み合わせることができますが、それはプロセスを複雑にし、手動でhttp://site.tld/en/newsのURLをhttp://site.tld/de/newsに変更し、ニュースページがドイツ語に変更されることを期待する人々のみに対応できます。

しかし、この場合でも、クッキーの値(以前の言語の選択に関する情報を含む)を使用することで、魔法や希望を少なくして実装することができる可能性があります。

どのアプローチを使用しますか?

あなたが既に推測したかもしれないように、私はhttp://site.tld/[:language]/[:query]をより賢明なオプションとして推薦するでしょう。

また実際のWordの状況では、URLの3番目の主要部分があるでしょう: "title"。オンラインショップでの商品の名前またはニュースサイトでの記事の見出しのように。

例:http://site.tld/en/news/article/121415/EU-as-global-reserve-currency

この場合、'/news/article/121415'がクエリになり、'EU-as-global-reserve-currency'がタイトルになります。純粋にSEO目的のためです。

それはLaravelでできますか?

Kinda、しかしデフォルトではありません。

私はそれに精通していません、しかし私が見たことから、Laravelは単純なパターンベースのルーティングメカニズムを使います。多言語URLを実装するには、多分 コアクラスを拡張する しなければならないでしょう。多言語ルーティングは異なる形式のストレージ(データベース、キャッシュおよび/または設定ファイル)にアクセスする必要があるからです。

それはルーティングされました。今何ですか?

結局、現在の言語と翻訳されたクエリの2つの有益な情報が得られます。これらの値は、結果を生成するクラスにディスパッチするために使用されます。

基本的には、次のURL:http://site.tld/ru/blog/novinka(または'/ru'がないバージョン)は以下のようになります。

$parameters = [
   'language' => 'ru',
   'classname' => 'blog',
   'method' => 'latest',
];

あなたはただ派遣するためにそれを使う:

$instance = new {$parameter['classname']};
$instance->{'get'.$parameters['method']}( $parameters );

特定の実装に応じて、またはそのバリエーションもあります。

102
tereško

Thomas Bleyによって提案されたプリプロセッサを使用したパフォーマンスヒットなしのi18nの実装

職場では、最近、いくつかのプロパティでi18nの実装を行いましたが、苦労し続けていたのは、オンザフライの翻訳を処理するパフォーマンスヒットでした Thomas Bley によるこの素晴らしいブログ投稿は、パフォーマンスの問題を最小限に抑えながら、大量のトラフィック負荷を処理するためにi18nを使用する方法を刺激しました。

PHPでわかるようにすべての翻訳操作に対して関数を呼び出す代わりに、ベースファイルをプレースホルダーで定義し、プリプロセッサを使用してそれらのファイルをキャッシュします(ファイルの変更時間を常に最新のコンテンツを提供していることを確認してください)。

翻訳タグ

Thomasは{tr}および{/tr}タグを使用して、翻訳の開始位置と終了位置を定義します。 TWIGを使用しているという事実により、混乱を避けるために{を使用したくないため、代わりに[%tr%][%/tr%]を使用します。基本的に、これは次のようになります。

`return [%tr%]formatted_value[%/tr%];`

Thomasは、ファイルで基本英語を使用することを提案していることに注意してください。英語で値を変更する場合、すべての翻訳ファイルを変更する必要がないため、これは行いません。

INIファイル

次に、各言語のINIファイルをplaceholder = translated形式で作成します。

// lang/fr.ini
formatted_value = number_format($value * Model_Exchange::getEurRate(), 2, ',', ' ') . '€'

// lang/en_gb.ini
formatted_value = '£' . number_format($value * Model_Exchange::getStgRate())

// lang/en_us.ini
formatted_value = '$' . number_format($value)

ユーザーがCMS内でこれらを変更し、preg_splitまたは\n=によってキーペアを取得し、CMSがINIファイル。

プリプロセッサコンポーネント

本質的に、トーマスはこのようなジャストインタイムの「コンパイラー」(実際にはプリプロセッサーです)関数を使用して翻訳ファイルを取得し、ディスク上に静的PHPファイルを作成することをお勧めします。この方法では、ファイル内のすべての文字列に対して翻訳関数を呼び出す代わりに、翻訳されたファイルを本質的にキャッシュします。

// This function was written by Thomas Bley, not by me
function translate($file) {
  $cache_file = 'cache/'.LANG.'_'.basename($file).'_'.filemtime($file).'.php';
  // (re)build translation?
  if (!file_exists($cache_file)) {
    $lang_file = 'lang/'.LANG.'.ini';
    $lang_file_php = 'cache/'.LANG.'_'.filemtime($lang_file).'.php';

    // convert .ini file into .php file
    if (!file_exists($lang_file_php)) {
      file_put_contents($lang_file_php, '<?php $strings='.
        var_export(parse_ini_file($lang_file), true).';', LOCK_EX);
    }
    // translate .php into localized .php file
    $tr = function($match) use (&$lang_file_php) {
      static $strings = null;
      if ($strings===null) require($lang_file_php);
      return isset($strings[ $match[1] ]) ? $strings[ $match[1] ] : $match[1];
    };
    // replace all {t}abc{/t} by tr()
    file_put_contents($cache_file, preg_replace_callback(
      '/\[%tr%\](.*?)\[%\/tr%\]/', $tr, file_get_contents($file)), LOCK_EX);
  }
  return $cache_file;
}

注:正規表現が機能することを確認しませんでした。会社のサーバーからコピーしませんでしたが、操作の仕組みを確認できます。

それを呼び出す方法

繰り返しますが、この例はThomas Bleyからのものであり、私からのものではありません。

// instead of
require("core/example.php");
echo (new example())->now();

// we write
define('LANG', 'en_us');
require(translate('core/example.php'));
echo (new example())->now();

Cookie(またはCookieを取得できない場合はセッション変数)に言語を保存し、リクエストごとに言語を取得します。これをオプションの$_GETパラメータと組み合わせて言語をオーバーライドできますが、サブページごとの言語またはページごとの言語はお勧めしません。インバウンドリンクの広がりを抑えるため、インバウンドリンクの価値を減らします。

この方法を使用する理由

この前処理方法は、次の3つの理由で気に入っています。

  1. まれにしか変更されないコンテンツに対して、多くの関数を呼び出さないことによるパフォーマンスの大幅な向上(このシステムでは、フランス語で100,000人の訪問者が翻訳の置換を1回しか実行しません)。
  2. 単純なフラットファイルを使用し、純粋なPHPソリューションであるため、データベースに負荷を追加しません。
  3. 翻訳内でPHP式を使用する機能。

翻訳されたデータベースコンテンツの取得

データベースにlanguageというコンテンツの列を追加し、先ほど定義したLANG定数にアクセサメソッドを使用するので、SQL呼び出し(ZF1を使用)は次のようになります。 :

$query = select()->from($this->_name)
                 ->where('language = ?', User::getLang())
                 ->where('id       = ?', $articleId)
                 ->limit(1);

記事にはidlanguageの複合主キーがあるため、記事54はすべての言語に存在できます。指定しない場合、LANGen_USにデフォルト設定されます。

URLスラッグ翻訳

ここで2つのことを組み合わせます。1つはbootstrapの関数で、言語の$_GETパラメーターを受け入れてCookie変数をオーバーライドし、もう1つは複数のスラッグを受け入れるルーティングです。その後、ルーティングで次のようなことができます。

"/wilkommen" => "/welcome/lang/de"
... etc ...

これらは、管理パネルから簡単に書き込むことができるフラットファイルに保存できます。 JSONまたはXMLは、それらをサポートするための適切な構造を提供する場合があります。

その他のいくつかのオプションに関する注意

PHPベースのオンザフライ翻訳

これらが前処理された翻訳よりも有利であるとは思えません。

フロントエンドベースの翻訳

私は長い間これらを面白いと思ってきましたが、いくつか注意点があります。たとえば、翻訳する予定のWebサイト上のフレーズのリスト全体をユーザーが利用できるようにする必要があります。これは、サイトの領域を非表示にしたり、アクセスを許可していない場合に問題になる可能性があります。

また、あなたのすべてのユーザーがあなたのサイトでJavascriptを喜んで使用できると仮定する必要がありますが、私の統計から、私たちのユーザーの約2.5%はそれなしで実行しています(またはNoscriptを使用してサイトの使用をブロックします) 。

データベース駆動型翻訳

PHPのデータベース接続速度は、書くべきものではありません。これは、翻訳するすべてのフレーズで関数を呼び出すという既に高いオーバーヘッドを追加します。このアプローチでは、パフォーマンスとスケーラビリティの問題が圧倒されるようです。

50
Glitch Desire

私はあなたが車輪を発明してgettextとISO言語の略語リストを使わないことを勧めます。国際化/ l10nが一般的なCMSやフレームワークにどのように実装されているかを見たことがありますか?

Gettextを使用すると、複数形の数のようにすでに多くのケースが実装されている強力なツールが得られます。英語では、単数形と複数形の2つの選択肢しかありません。しかし例えばロシア語には3つの形式があり、それは英語ほど単純ではありません。

また多くの翻訳者はすでにgettextを扱う経験があります。

CakePHP または Drupal をご覧ください。どちらも多言語対応。インターフェースのローカライゼーションの例としてのCakePHPとコンテンツ翻訳の例としてのDrupal。

データベースを使用するl10nの場合はまったくそうではありません。それはクエリにトンになります。標準的なアプローチは、初期段階で(あるいは遅延ロードを好む場合は最初にi10n関数を呼び出すときに)、すべてのl10nデータをメモリに取得することです。それは一度に.poファイルまたはDBからすべてのデータを読み取ることができます。そして単に配列から要求された文字列を読むだけではありません。

あなたがインタフェースを翻訳するためにオンラインツールを実装する必要があるならば、あなたはそれを扱うためにまだすべてのデータをファイルに保存するよりもDBにそのデータを持つことができます。メモリ内のデータ量を減らすために、翻訳されたすべてのメッセージ/文字列をグループに分割し、必要に応じて必要なグループだけを読み込むことができます。

それであなたはあなたの#3で全く正しいです。 1つ例外があります。通常、これはコントローラごとのファイルではなく、1つの大きなファイルです。一つのファイルを開くのがパフォーマンス上最適だからです。 include/requireが呼び出されたときのファイル操作を回避するために、一部の高負荷のWebアプリケーションはすべてのPHPコードを1つのファイルにまとめてコンパイルしていることをご存知でしょう。

URLについて Googleは間接的に推奨します 翻訳を使用する:

フランス語の内容を明確に示すために、 http://example.ca/fr/vélo-de-montagne.html

また、私はあなたがデフォルトの言語プレフィックスにユーザーをリダイレクトする必要があると思います。 http://examlpe.com/about-us にリダイレクトされます http://examlpe.com/en/about-us しかしあなたのサイトが1つの言語しか使用していない場合接頭辞はまったく必要ありません。

チェックアウト: http://www.audiomicro.com/trailer-hit-impact-psychodrama-sound-effects-836925http://nl.audiomicro.com/aanhangwagen-hit -effect-psychodrama-geluidseffecten-836925http://de.audiomicro.com/anhanger-hit-auswirkungen-psychodrama-sound-effekte-836925

コンテンツを翻訳するのはもっと難しい仕事です。私はそれが異なるタイプのコンテンツとのいくつかの違いになると思います。記事、メニュー項目などしかし#4では正しい方向に進んでいます。もっとDrupalを見てアイデアを得てください。それは十分に明確なDBスキーマと翻訳に十分なインタフェースを持っています。あなたが記事を作成し、それに言語を選択するように。そしてあなたが後でそれを他の言語に翻訳することができます。

Drupal translation interface

URLスラッグでは問題ないと思います。ナメクジ用に別のテーブルを作成するだけで、正しい判断になります。正しいインデックスを使用しても、大量のデータがあってもテーブルをクエリするのは問題ありません。全文検索ではなく、slugにvarcharデータ型を使用する場合は文字列が一致し、そのフィールドにもインデックスを付けることができます。

PS申し訳ありませんが、私の英語は完璧とは言い難いものです。

14
Yaroslav

それはあなたのウェブサイトがどれだけのコンテンツを持っているかによって異なります。最初はここで他のすべての人と同じようにデータベースを使用しましたが、データベースのすべての動作をスクリプト化するのは時間がかかる可能性があります。これは理想的な方法ではないと思います。特にテキストが多い場合はこの方法を使用します。ただし、ユーザーがデータを入力することはできません。これは翻訳ファイルとして使われます。しかし、あなたが自分で翻訳を追加すれば、うまくいくでしょう。

このテキストがあるとしましょう。

Welcome!

これを翻訳付きのデータベースに入力することもできますが、これを行うこともできます。

$welcome = array(
"English"=>"Welcome!",
"German"=>"Willkommen!",
"French"=>"Bienvenue!",
"Turkish"=>"Hoşgeldiniz!",
"Russian"=>"Добро пожаловать!",
"Dutch"=>"Welkom!",
"Swedish"=>"Välkommen!",
"Basque"=>"Ongietorri!",
"Spanish"=>"Bienvenito!"
"Welsh"=>"Croeso!");

今、あなたのウェブサイトがクッキーを使用しているなら、あなたはこれを例えば持っています:

$_COOKIE['language'];

簡単にするために、簡単に使えるコードに変換しましょう。

$language=$_COOKIE['language'];

あなたのクッキーの言語がウェールズ語で、あなたがこのコードを持っているなら:

echo $welcome[$language];

この結果は次のようになります。

Croeso!

あなたがあなたのウェブサイトのためにたくさんの翻訳を追加する必要があり、データベースがあまりにも消費するならば、配列を使うことは理想的な解決策になることができます。

10
user3749746

私はあなたが翻訳のために本当にデータベースに頼らないことをお勧めします。

私は前に同じような問題に直面していたし、私の問題を解決するために次のクラスを書いた

オブジェクト:Locale\Locale

<?php

  namespace Locale;

  class Locale{

// Following array stolen from Zend Framework
public $country_to_locale = array(
    'AD' => 'ca_AD',
    'AE' => 'ar_AE',
    'AF' => 'fa_AF',
    'AG' => 'en_AG',
    'AI' => 'en_AI',
    'AL' => 'sq_AL',
    'AM' => 'hy_AM',
    'AN' => 'pap_AN',
    'AO' => 'pt_AO',
    'AQ' => 'und_AQ',
    'AR' => 'es_AR',
    'AS' => 'sm_AS',
    'AT' => 'de_AT',
    'AU' => 'en_AU',
    'AW' => 'nl_AW',
    'AX' => 'sv_AX',
    'AZ' => 'az_Latn_AZ',
    'BA' => 'bs_BA',
    'BB' => 'en_BB',
    'BD' => 'bn_BD',
    'BE' => 'nl_BE',
    'BF' => 'mos_BF',
    'BG' => 'bg_BG',
    'BH' => 'ar_BH',
    'BI' => 'rn_BI',
    'BJ' => 'fr_BJ',
    'BL' => 'fr_BL',
    'BM' => 'en_BM',
    'BN' => 'ms_BN',
    'BO' => 'es_BO',
    'BR' => 'pt_BR',
    'BS' => 'en_BS',
    'BT' => 'dz_BT',
    'BV' => 'und_BV',
    'BW' => 'en_BW',
    'BY' => 'be_BY',
    'BZ' => 'en_BZ',
    'CA' => 'en_CA',
    'CC' => 'ms_CC',
    'CD' => 'sw_CD',
    'CF' => 'fr_CF',
    'CG' => 'fr_CG',
    'CH' => 'de_CH',
    'CI' => 'fr_CI',
    'CK' => 'en_CK',
    'CL' => 'es_CL',
    'CM' => 'fr_CM',
    'CN' => 'zh_Hans_CN',
    'CO' => 'es_CO',
    'CR' => 'es_CR',
    'CU' => 'es_CU',
    'CV' => 'kea_CV',
    'CX' => 'en_CX',
    'CY' => 'el_CY',
    'CZ' => 'cs_CZ',
    'DE' => 'de_DE',
    'DJ' => 'aa_DJ',
    'DK' => 'da_DK',
    'DM' => 'en_DM',
    'DO' => 'es_DO',
    'DZ' => 'ar_DZ',
    'EC' => 'es_EC',
    'EE' => 'et_EE',
    'EG' => 'ar_EG',
    'EH' => 'ar_EH',
    'ER' => 'ti_ER',
    'ES' => 'es_ES',
    'ET' => 'en_ET',
    'FI' => 'fi_FI',
    'FJ' => 'hi_FJ',
    'FK' => 'en_FK',
    'FM' => 'chk_FM',
    'FO' => 'fo_FO',
    'FR' => 'fr_FR',
    'GA' => 'fr_GA',
    'GB' => 'en_GB',
    'Gd' => 'en_Gd',
    'GE' => 'ka_GE',
    'GF' => 'fr_GF',
    'GG' => 'en_GG',
    'GH' => 'ak_GH',
    'GI' => 'en_GI',
    'GL' => 'iu_GL',
    'GM' => 'en_GM',
    'GN' => 'fr_GN',
    'GP' => 'fr_GP',
    'GQ' => 'fan_GQ',
    'GR' => 'el_GR',
    'GS' => 'und_GS',
    'GT' => 'es_GT',
    'GU' => 'en_GU',
    'GW' => 'pt_GW',
    'GY' => 'en_GY',
    'HK' => 'zh_Hant_HK',
    'HM' => 'und_HM',
    'HN' => 'es_HN',
    'HR' => 'hr_HR',
    'HT' => 'ht_HT',
    'HU' => 'hu_HU',
    'ID' => 'id_ID',
    'IE' => 'en_IE',
    'IL' => 'he_IL',
    'IM' => 'en_IM',
    'IN' => 'hi_IN',
    'IO' => 'und_IO',
    'IQ' => 'ar_IQ',
    'IR' => 'fa_IR',
    'IS' => 'is_IS',
    'IT' => 'it_IT',
    'JE' => 'en_JE',
    'JM' => 'en_JM',
    'JO' => 'ar_JO',
    'JP' => 'ja_JP',
    'KE' => 'en_KE',
    'KG' => 'ky_Cyrl_KG',
    'KH' => 'km_KH',
    'KI' => 'en_KI',
    'KM' => 'ar_KM',
    'KN' => 'en_KN',
    'KP' => 'ko_KP',
    'KR' => 'ko_KR',
    'KW' => 'ar_KW',
    'KY' => 'en_KY',
    'KZ' => 'ru_KZ',
    'LA' => 'lo_LA',
    'LB' => 'ar_LB',
    'LC' => 'en_LC',
    'LI' => 'de_LI',
    'LK' => 'si_LK',
    'LR' => 'en_LR',
    'LS' => 'st_LS',
    'LT' => 'lt_LT',
    'LU' => 'fr_LU',
    'LV' => 'lv_LV',
    'LY' => 'ar_LY',
    'MA' => 'ar_MA',
    'MC' => 'fr_MC',
    'MD' => 'ro_MD',
    'ME' => 'sr_Latn_ME',
    'MF' => 'fr_MF',
    'MG' => 'mg_MG',
    'MH' => 'mh_MH',
    'MK' => 'mk_MK',
    'ML' => 'bm_ML',
    'MM' => 'my_MM',
    'MN' => 'mn_Cyrl_MN',
    'MO' => 'zh_Hant_MO',
    'MP' => 'en_MP',
    'MQ' => 'fr_MQ',
    'MR' => 'ar_MR',
    'MS' => 'en_MS',
    'MT' => 'mt_MT',
    'MU' => 'mfe_MU',
    'MV' => 'dv_MV',
    'MW' => 'ny_MW',
    'MX' => 'es_MX',
    'MY' => 'ms_MY',
    'MZ' => 'pt_MZ',
    'NA' => 'kj_NA',
    'NC' => 'fr_NC',
    'NE' => 'ha_Latn_NE',
    'NF' => 'en_NF',
    'NG' => 'en_NG',
    'NI' => 'es_NI',
    'NL' => 'nl_NL',
    'NO' => 'nb_NO',
    'NP' => 'ne_NP',
    'NR' => 'en_NR',
    'NU' => 'niu_NU',
    'NZ' => 'en_NZ',
    'OM' => 'ar_OM',
    'PA' => 'es_PA',
    'PE' => 'es_PE',
    'PF' => 'fr_PF',
    'PG' => 'tpi_PG',
    'PH' => 'fil_PH',
    'PK' => 'ur_PK',
    'PL' => 'pl_PL',
    'PM' => 'fr_PM',
    'PN' => 'en_PN',
    'PR' => 'es_PR',
    'PS' => 'ar_PS',
    'PT' => 'pt_PT',
    'PW' => 'pau_PW',
    'PY' => 'gn_PY',
    'QA' => 'ar_QA',
    'RE' => 'fr_RE',
    'RO' => 'ro_RO',
    'RS' => 'sr_Cyrl_RS',
    'RU' => 'ru_RU',
    'RW' => 'rw_RW',
    'SA' => 'ar_SA',
    'SB' => 'en_SB',
    'SC' => 'crs_SC',
    'SD' => 'ar_SD',
    'SE' => 'sv_SE',
    'SG' => 'en_SG',
    'SH' => 'en_SH',
    'SI' => 'sl_SI',
    'SJ' => 'nb_SJ',
    'SK' => 'sk_SK',
    'SL' => 'kri_SL',
    'SM' => 'it_SM',
    'SN' => 'fr_SN',
    'SO' => 'sw_SO',
    'SR' => 'srn_SR',
    'ST' => 'pt_ST',
    'SV' => 'es_SV',
    'SY' => 'ar_SY',
    'SZ' => 'en_SZ',
    'TC' => 'en_TC',
    'TD' => 'fr_TD',
    'TF' => 'und_TF',
    'TG' => 'fr_TG',
    'TH' => 'th_TH',
    'TJ' => 'tg_Cyrl_TJ',
    'TK' => 'tkl_TK',
    'TL' => 'pt_TL',
    'TM' => 'tk_TM',
    'TN' => 'ar_TN',
    'TO' => 'to_TO',
    'TR' => 'tr_TR',
    'TT' => 'en_TT',
    'TV' => 'tvl_TV',
    'TW' => 'zh_Hant_TW',
    'TZ' => 'sw_TZ',
    'UA' => 'uk_UA',
    'UG' => 'sw_UG',
    'UM' => 'en_UM',
    'US' => 'en_US',
    'UY' => 'es_UY',
    'UZ' => 'uz_Cyrl_UZ',
    'VA' => 'it_VA',
    'VC' => 'en_VC',
    'VE' => 'es_VE',
    'VG' => 'en_VG',
    'VI' => 'en_VI',
    'VN' => 'vn_VN',
    'VU' => 'bi_VU',
    'WF' => 'wls_WF',
    'WS' => 'sm_WS',
    'YE' => 'ar_YE',
    'YT' => 'swb_YT',
    'ZA' => 'en_ZA',
    'ZM' => 'en_ZM',
    'ZW' => 'sn_ZW'
);

/**
 * Store the transaltion for specific languages
 *
 * @var array
 */
protected $translation = array();

/**
 * Current locale
 *
 * @var string
 */
protected $locale;

/**
 * Default locale
 *
 * @var string
 */
protected $default_locale;

/**
 *
 * @var string
 */
protected $locale_dir;

/**
 * Construct.
 *
 *
 * @param string $locale_dir            
 */
public function __construct($locale_dir)
{
    $this->locale_dir = $locale_dir;
}

/**
 * Set the user define localte
 *
 * @param string $locale            
 */
public function setLocale($locale = null)
{
    $this->locale = $locale;

    return $this;
}

/**
 * Get the user define locale
 *
 * @return string
 */
public function getLocale()
{
    return $this->locale;
}

/**
 * Get the Default locale
 *
 * @return string
 */
public function getDefaultLocale()
{
    return $this->default_locale;
}

/**
 * Set the default locale
 *
 * @param string $locale            
 */
public function setDefaultLocale($locale)
{
    $this->default_locale = $locale;

    return $this;
}

/**
 * Determine if transltion exist or translation key exist
 *
 * @param string $locale            
 * @param string $key            
 * @return boolean
 */
public function hasTranslation($locale, $key = null)
{
    if (null == $key && isset($this->translation[$locale])) {
        return true;
    } elseif (isset($this->translation[$locale][$key])) {
        return true;
    }

    return false;
}

/**
 * Get the transltion for required locale or transtion for key
 *
 * @param string $locale            
 * @param string $key            
 * @return array
 */
public function getTranslation($locale, $key = null)
{
    if (null == $key && $this->hasTranslation($locale)) {
        return $this->translation[$locale];
    } elseif ($this->hasTranslation($locale, $key)) {
        return $this->translation[$locale][$key];
    }

    return array();
}

/**
 * Set the transtion for required locale
 *
 * @param string $locale
 *            Language code
 * @param string $trans
 *            translations array
 */
public function setTranslation($locale, $trans = array())
{
    $this->translation[$locale] = $trans;
}

/**
 * Remove transltions for required locale
 *
 * @param string $locale            
 */
public function removeTranslation($locale = null)
{
    if (null === $locale) {
        unset($this->translation);
    } else {
        unset($this->translation[$locale]);
    }
}

/**
 * Initialize locale
 *
 * @param string $locale            
 */
public function init($locale = null, $default_locale = null)
{
    // check if previously set locale exist or not
    $this->init_locale();
    if ($this->locale != null) {
        return;
    }

    if ($locale == null || (! preg_match('#^[a-z]+_[a-zA-Z_]+$#', $locale) && ! preg_match('#^[a-z]+_[a-zA-Z]+_[a-zA-Z_]+$#', $locale))) {
        $this->detectLocale();
    } else {
        $this->locale = $locale;
    }

    $this->init_locale();
}

/**
 * Attempt to autodetect locale
 *
 * @return void
 */
private function detectLocale()
{
    $locale = false;

    // GeoIP
    if (function_exists('geoip_country_code_by_name') && isset($_SERVER['REMOTE_ADDR'])) {

        $country = geoip_country_code_by_name($_SERVER['REMOTE_ADDR']);

        if ($country) {

            $locale = isset($this->country_to_locale[$country]) ? $this->country_to_locale[$country] : false;
        }
    }

    // Try detecting locale from browser headers
    if (! $locale) {

        if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {

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

            foreach ($languages as $lang) {

                $lang = str_replace('-', '_', trim($lang));

                if (strpos($lang, '_') === false) {

                    if (isset($this->country_to_locale[strtoupper($lang)])) {

                        $locale = $this->country_to_locale[strtoupper($lang)];
                    }
                } else {

                    $lang = explode('_', $lang);

                    if (count($lang) == 3) {
                        // language_Encoding_COUNTRY
                        $this->locale = strtolower($lang[0]) . ucfirst($lang[1]) . strtoupper($lang[2]);
                    } else {
                        // language_COUNTRY
                        $this->locale = strtolower($lang[0]) . strtoupper($lang[1]);
                    }

                    return;
                }
            }
        }
    }

    // Resort to default locale specified in config file
    if (! $locale) {
        $this->locale = $this->default_locale;
    }
}

/**
 * Check if config for selected locale exists
 *
 * @return void
 */
private function init_locale()
{
    if (! file_exists(sprintf('%s/%s.php', $this->locale_dir, $this->locale))) {
        $this->locale = $this->default_locale;
    }
}

/**
 * Load a Transtion into array
 *
 * @return void
 */
private function loadTranslation($locale = null, $force = false)
{
    if ($locale == null)
        $locale = $this->locale;

    if (! $this->hasTranslation($locale)) {
        $this->setTranslation($locale, include (sprintf('%s/%s.php', $this->locale_dir, $locale)));
    }
}

/**
 * Translate a key
 *
 * @param
 *            string Key to be translated
 * @param
 *            string optional arguments
 * @return string
 */
public function translate($key)
{
    $this->init();
    $this->loadTranslation($this->locale);

    if (! $this->hasTranslation($this->locale, $key)) {

        if ($this->locale !== $this->default_locale) {

            $this->loadTranslation($this->default_locale);

            if ($this->hasTranslation($this->default_locale, $key)) {

                $translation = $this->getTranslation($this->default_locale, $key);
            } else {
                // return key as it is or log error here
                return $key;
            }
        } else {
            return $key;
        }
    } else {
        $translation = $this->getTranslation($this->locale, $key);
    }
    // Replace arguments
    if (false !== strpos($translation, '{a:')) {
        $replace = array();
        $args = func_get_args();
        for ($i = 1, $max = count($args); $i < $max; $i ++) {
            $replace['{a:' . $i . '}'] = $args[$i];
        }
        // interpolate replacement values into the messsage then return
        return strtr($translation, $replace);
    }

    return $translation;
  }
}

使用法

 <?php
    ## /locale/en.php

    return array(
       'name' => 'Hello {a:1}'
       'name_full' => 'Hello {a:1} {a:2}'
   );

$locale = new Locale(__DIR__ . '/locale');
$locale->setLocale('en');// load en.php from locale dir
//want to work with auto detection comment $locale->setLocale('en');

echo $locale->translate('name', 'Foo');
echo $locale->translate('name', 'Foo', 'Bar');

使い方

{a:1}は、メソッドに渡される1番目の引数に置き換えられますLocale::translate('key_name','arg1'){a:2}は、メソッドLocale::translate('key_name','arg1','arg2')に渡される2番目の引数に置き換えられます

検出のしくみ

  • デフォルトではgeoipがインストールされていればgeoip_country_code_by_nameで国別コードを返し、geoipがインストールされていなければHTTP_ACCEPT_LANGUAGEヘッダーへのフォールバック
7
Shushant

副次的な答え:それらの前に言語識別子を付けて翻訳されたURLを絶対に使用してください。 http://www.domain.com/nl/over-ons
ハイブリッドソリューションは複雑になる傾向があるので、私はそれに固執するだけです。どうして?原因URLはSEOに不可欠です。

Dbの翻訳について:言語の数は多かれ少なかれ固定されていますか?それとも、予測不可能で動的なのか?それが修正されるならば、私はただ新しい列を追加するでしょう、そうでなければ複数のテーブルで行きます。

しかし、一般的に、なぜDrupalを使わないのですか?誰もが独自のCMSを構築したいと考えています。それは、より速く、よりスリムなどです。しかし、それは本当に悪い考えです!

5
Remy

私はすでに与えられた答えを洗練しようとするつもりはありません。代わりに、私自身のOOP PHPフレームワークが翻訳を処理する方法についてお話します。

内部的には、私のフレームワークはen、fr、es、cnなどのようなコードを使います。配列はWebサイトでサポートされている言語を保持します。array( 'en'、 'fr'、 'es'、 'cn')言語コードは$ _GET(lang = fr)で渡されます。配列の最初の言語に設定されます。そのため、プログラムの実行中および最初からいつでも、現在の言語がわかります。

典型的なアプリケーションで翻訳される必要がある内容の種類を理解することは役に立ちます:

1)クラスからのエラーメッセージ(または手続き型コード)2)クラスからの非エラーメッセージ(または手続き型コード)3)ページコンテンツ(通常はデータベースに格納)4)サイト全体の文字列(Webサイト名など)5)スクリプト - 特定の文字列

最初のタイプは理解するのが簡単です。基本的に、 "データベースに接続できませんでした..."のようなメッセージについて話しています。これらのメッセージは、エラーが発生したときにのみロードする必要があります。私のマネージャクラスは他のクラスからの呼び出しを受け取り、パラメータとして渡された情報を使用することは単に関連するクラスフォルダに行き、エラーファイルを取得します。

2番目のタイプのエラーメッセージは、フォームの検証に失敗したときに表示されるメッセージに似ています。 (「...は空欄にできません」または「5文字以上のパスワードを選択してください」)。クラスを実行する前に文字列をロードする必要があります。

実際のページコンテンツでは、言語ごとに1つのテーブルを使用します。各テーブルの先頭には言語のコードが付きます。そのため、en_contentは英語の内容を含む表、es_contentはスペイン用、cn_contentは中国用、そしてfr_contentはフランス用です。

4番目の種類の文字列は、Webサイト全体に関連しています。これは言語のコードを使って命名された設定ファイル、つまりen_lang.php、es_lang.phpなどを通してロードされます。グローバル言語ファイルでは、アレイ( '英語'、 '中国語'、 'スペイン語'、 'フランス語')などの翻訳言語を英語のグローバルファイルおよびアレイ( 'Anglais'、 'C​​hinois'、 ')で読み込む必要があります。フランス語ファイルのEspagnol '、' Francais ')。そのため、言語選択用のドロップダウンを作成すると、それは正しい言語になります。

最後に、スクリプト固有の文字列があります。あなたが料理用のアプリケーションを書くのであれば、それは「あなたのオーブンは十分に熱くなかった」かもしれません。

私のアプリケーションサイクルでは、グローバル言語ファイルが最初にロードされます。そこにはグローバルな文字列( "Jack's Website"のような)だけでなく、いくつかのクラスの設定もあります。基本的に言語や文化に依存するものなら何でも。そこに含まれる文字列の中には、日付のマスク(MMDDYYYYまたはDDMMYYYY)、またはISO言語コードが含まれています。メインの言語ファイルには、クラスが非常に少ないため、個々のクラスの文字列を含めます。

ディスクから読み取られる2番目と最後の言語ファイルは、スクリプト言語ファイルです。 lang_en_home_welcome.phpは、home/welcomeスクリプトの言語ファイルです。スクリプトはモード(home)とアクション(welcome)によって定義されています。各スクリプトには、configファイルとlangファイルを含む独自のフォルダがあります。

このスクリプトは、前述のように、コンテンツテーブルの名前を付けてデータベースからコンテンツを取得します。

問題が発生した場合、マネージャは言語に依存するエラーファイルの入手先を知っています。そのファイルはエラーの場合にのみロードされます。

だから結論は明らかです。アプリケーションやフレームワークの開発を始める前に、翻訳の問題について考えてください。翻訳を組み込んだ開発ワークフローも必要です。私のフレームワークでは、サイト全体を英語で開発してから、すべての関連ファイルを翻訳します。

翻訳文字列が実装される方法についての簡単な最後のWord。私のフレームワークは単一のグローバル、$ managerを持っていて、それは他のどのサービスにも利用可能なサービスを実行します。たとえば、フォームサービスはHTMLサービスを取得し、それを使用してHTMLを作成します。私のシステムのサービスの1つは翻訳者サービスです。 $ translator-> set($ service、$ code、$ string)は現在の言語の文字列を設定します。言語ファイルはそのようなステートメントのリストです。 $ translator-> get($ service、$ code)は翻訳文字列を取得します。 $ codeは、1のような数値または 'no_connection'のような文字列です。トランスレータのデータ領域にはそれぞれ独自のネームスペースがあるため、サービス間の衝突はありません。

私が数年前にしなければならなかったようにそれが誰かが車輪を再発明するタスクを救うであろうという望みでこれをここにポストする。

4
JG Estiot

Symfony フレームワークを使い始める前に、私はしばらく前に同じ問題を抱えていました。

  1. PageId(または#2で説明されているobjectId、objectTable)、ターゲット言語、およびフォールバック(デフォルト)言語のオプションパラメータを持つ関数__()を使用するだけです。後で変更するためのより簡単な方法を持つために、デフォルトの言語を何らかのグローバル設定で設定することができます。

  2. データベースにコンテンツを格納するために、私は以下の構造を使用しました:( pageId、言語、コンテンツ、変数)。

    • pageIdはあなたが翻訳したいあなたのページへのFKです。ニュース、ギャラリーなど、他のオブジェクトがある場合は、それを2つのフィールドobjectId、objectTableに分割します。

    • language - 明らかにISO言語の文字列EN_en、LT_lt、EN_usなどを格納します。

    • content - 変数置換用のワイルドカードと一緒に翻訳したいテキスト。例 "Hello mr。%% name %%。アカウントの残高は%% balance %%です。"

    • variables - JSONでエンコードされた変数PHPはこれらを素早く解析するための関数を提供します。例 "name:Laurynas、balance:15.23"です。

    • ナメクジ畑も言及しました。あなたはそれをこのテーブルに自由に追加して、それを素早く検索することができます。

  3. データベース呼び出しは、翻訳をキャッシュすることで最小限に抑える必要があります。 PHP言語では最速の構造であるため、PHP配列に格納する必要があります。どのようにしてこのキャッシングを行うかはあなた次第です。私の経験からすると、サポートされている各言語用のフォルダーと各pageId用の配列が必要です。翻訳を更新した後にキャッシュを再構築する必要があります。変更された配列のみを再生成する必要があります。

  4. #2で答えたと思う

  5. あなたの考えは完全に論理的です。これは非常に簡単です、そして、私はあなたにどんな問題もしないと思います。

URLは、変換テーブルに格納されているスラッグを使用して変換する必要があります。

最後の言葉

ベストプラクティスを調査することは常に良いことですが、ホイールを再発明しないでください。よく知られているフレームワークからコンポーネントを取り出して使用するだけです。

Symfonyの翻訳コンポーネント を見てください。それはあなたにとって良いコードベースでしょう。

私は何度も何度も自分自身に関連した質問をしてきました、それから正式な言語で迷子になりました...しかし、ちょっとあなたを少し手助けするために私はいくつかの発見を共有したいと思います:

高度なCMSを見てみることをお勧めします

PHPに対するTypo3(私はたくさんのものがあることを知っていますが、それは私が最も成熟していると思うものです)

Plone内のPython

2013年のWebの動作がそれとは異なることがわかった場合は、最初からやり直してください。それは新しいCMSを構築するために非常に熟練した/経験豊富な人々のチームをまとめることを意味するでしょう。その目的のためにポリマーを見たいと思うかもしれません。

コーディングや多言語のWebサイトや母国語のサポートに関しては、すべてのプログラマーがUnicodeについての手がかりを持つべきだと思います。あなたがUnicodeを知らないならば、あなたは最も確実にあなたのデータを台無しにするでしょう。何千ものISOコードを使わないでください。彼らはあなたにいくつかのメモリを節約します。しかし、あなたは文字通りUTF-8ですべてのことを行うことができます。しかしそのためには、基本的にはutf-16またはutf-32にする2または4バイト文字を格納する必要があります。

URLエンコーディングに関するものであれば、エンコーディングを混在させてはいけません。少なくともドメイン名については、ブラウザのようなアプリケーションを提供するさまざまなロビーによって定義された規則があることに注意してください。例えばドメインは次のように非常によく似ています。

ьankofamerica.comまたはbankofamerica.com samesamebutdifferent;)

もちろん、すべてのエンコーディングを扱うためにはファイルシステムが必要です。 utf - 8ファイルシステムを使用してUnicodeのためのもう一つのプラス。

翻訳についての場合は、文書の構造について考えてください。例えば本や記事。これらの構造について理解するためのdocbookの指定があります。しかしHTMLでは、コンテンツブロックについてだけです。それで、あなたはそのレベルで、またウェブページレベルまたはドメインレベルで翻訳をしたいです。そのため、ブロックが存在しない場合、存在しない場合、Webページが存在しない場合は、上位のナビゲーションレベルにリダイレクトされます。ドメインのナビゲーション構造が完全に異なる場合は、完全に異なる構造を管理する必要があります。これはTypo3で既にできます。

そのフレームワークについて、私が知っている最も成熟したものは、MVCのような一般的なことをするために(私は本当にそれが嫌いです! "パフォーマンス"のように何かを販売したい場合は、Wordのパフォーマンスを使用してください。地獄はZendです。 php chaos coderに標準を取り入れることは良いことであることが証明されています。しかし、typo3にはCMS以外にフレームワークもあります。最近それが再開発され、現在flow3と呼ばれています。フレームワークはもちろんデータベースの抽象化、テンプレート化、そしてキャッシングのための概念をカバーしていますが、それぞれに長所があります。

それがキャッシングについてであるならば...それはひどく複雑になる/多層になることができます。 PHPでは、アクセラレータ、オペコードだけでなく、html、httpd、mysql、xml、css、jsなど、あらゆる種類のキャッシュについて考えることができます。もちろん、いくつかの部分はキャッシュされるべきで、ブログの回答のような動的な部分はそうすべきではありません。生成されたURLを使用してAJAXでリクエストする必要があります。 JSON、hashbangsなど.

それから、あなたはあなたのウェブサイト上の特定のsersによってのみアクセスまたは管理される小さなコンポーネントを持ちたいと思うので、概念的にそれが大きな役割を果たします。

また、あなたはstatisticsを作りたいのですが、おそらく分散システム/ facebookのfacebookなど、あなたの上のcms上に構築されるソフトウェアを持っているでしょう...それであなたは異なるタイプのデータベースを必要としますinmemory、bigdata、xml、何でも。

まあ、私は今のところそれで十分だと思います。 typo3/ploneも言及されたフレームワークも聞いたことがなければ、勉強するのに十分なものです。その道には、まだ質問していない質問に対する解決策がたくさんあります。

それなら、その2013年とphpがとにかく死ぬことになっているので、新しいCMSを作ろう。そうすれば、迷わずに他の開発者グループに加わることができます。

がんばろう!

そしてところで。今後、Webサイトがなくなるのはどうでしょうか。そして私たちはみんなグーグルプラスになるでしょうか?開発者がもう少し独創的になり、何か便利なことをすることを願っています(喧嘩に同化されないように)

////編集///既存のアプリケーションについて少し考えてみます。

あなたがphp mysql CMSを持っていて、あなたがmultilangサポートを埋め込もうとしたなら。テーブルを任意の言語の追加列と一緒に使用するか、同じテーブルにオブジェクトIDと言語IDを使用して翻訳を挿入するか、または任意の言語に同じテーブルを作成してそこにオブジェクトを挿入しますそれらすべてを表示させます。データベースにはutf8 general ciを使用し、もちろんフロントエンド/バックエンドではutf8 text/encodingを使用してください。私はあなたがすでに説明した方法でURLのURLパスセグメントを使いました

domain.org/en/についてあなたはあなたのコンテンツテーブルにlang IDをマッピングすることができます。とにかくあなたのURLのためのパラメータのマップを持っている必要があるのであなたはあなたのURLのパスセグメントからマップされるべきパラメータを定義したいのです。

domain.org/en/about/employees/IT/administrators/

ルックアップ設定

ページID URL

1 | /about/employees/../ ..

1 | /../about/employees../../

パラメータをurl pathsegment ""にマップします

$parameterlist[lang] = array(0=>"nl",1=>"en"); // default nl if 0
$parameterlist[branch] = array(1=>"IT",2=>"DESIGN"); // default nl if 0
$parameterlist[employertype] = array(1=>"admin",1=>"engineer"); //could be a sql result 

$websiteconfig[]=$userwhatever;
$websiteconfig[]=$parameterlist;
$someparameterlist[] = array("branch"=>$someid);
$someparameterlist[] = array("employertype"=>$someid);
function getURL($someparameterlist){ 
// todo foreach someparameter lookup pathsegment 
return path;
}

一言で言うと、それはすでに上の記事で取り上げられています。

そして忘れないように、あなたはあなたの生成しているphpファイルにURLを "書き換える"必要があるでしょう。それはほとんどの場合index.phpです。

1
Dr. Dama