私はこの質問に何ヶ月も悩まされてきましたが、これまですべての可能なオプションを検討する必要がある状況に陥ったことはありませんでした。今は、可能性を知り、今後のプロジェクトで使用するための独自の好みを作成する時期だと感じています。
まず私が求めている状況を概説しましょう
かなり長い間使用してきたコンテンツ管理システムをアップグレード/再開発しようとしています。しかし、多言語化はこのシステムにとって大きな改善点だと感じています。以前はフレームワークを使用していませんでしたが、今後のプロジェクトでは Laraval4 を使用する予定です。PHP をコーディングするよりクリーンな方法としては、Laravel が最適な選択肢のようです。Sidenote: Laraval4 should be no factor in your answer
プラットフォーム/フレームワークに依存しない一般的な翻訳方法を探しています。
何を翻訳すべきか
私が探しているシステムは、できるだけユーザーフレンドリーである必要があるため、翻訳を管理する方法は CMS 内に存在する必要があります。翻訳ファイルや HTML/PHP 解析テンプレートを変更するために FTP 接続を開始する必要はないはずです。
さらに、追加のテーブルを作成する必要なく、複数のデータベース テーブルを変換する最も簡単な方法を探しています。
私が自分で考えたこと
すでに自分で検索したり、読んだり、試してみたりしています。選択肢はいくつかあります。しかし、自分が本当に求めているベスト プラクティス メソッドにたどり着いたとは思えません。今のところ、これが私が思いついた方法ですが、この方法にも副作用があります。
- PHP 解析テンプレート: テンプレート システムは PHP によって解析される必要があります。こうすることで、テンプレートを開いて変更することなく、翻訳されたパラメーターを HTML に挿入できます。さらに、PHP で解析されたテンプレートを使用すると、各言語のサブフォルダー (以前はそうでした) ではなく、Web サイト全体に対して 1 つのテンプレートを持つことができます。この目標を達成する方法は、Smarty、TemplatePower、Laravel の Blade、またはその他のテンプレート パーサーのいずれかです。前述したように、これは記述されたソリューションとは独立している必要があります。
- データベース駆動: おそらく、これについては再度言及する必要はないでしょう。しかし、ソリューションはデータベース駆動型である必要があります。CMS はオブジェクト指向と MVC を目的としているため、文字列の論理データ構造を考える必要があります。テンプレートは templates/Controller/View.php という構造になるため、おそらくこの構造が最も理にかなっています:
Controller.View.parameter
。データベース テーブルには、これらのフィールドと フィールドが含まれますvalue
。テンプレート内では、 のような何らかの sort メソッドを使用できecho __('Controller.View.welcome', array('name', 'Joshua'))
、パラメーターには が含まれますWelcome, :name
。したがって、結果は になりますWelcome, Joshua
。:name などのパラメーターはエディターで簡単に理解できるため、これはこれを行うのに適した方法のようです。 - データベース負荷が低い: もちろん、上記のシステムでは、これらの文字列が実行中に読み込まれると、データベースに大量の負荷がかかります。したがって、管理環境で言語ファイルが編集/保存されるとすぐに、言語ファイルを再レンダリングするキャッシュ システムが必要になります。ファイルが生成されるため、適切なファイル システム レイアウトも必要です。 または
languages/en_EN/Controller/View.php
.ini のどちらでも、最も適したものを使用できます。おそらく、.ini の方が最終的にはさらに速く解析されます。このフォルダーには のデータが含まれている必要がありますformat parameter=value;
。レンダリングされる各ビューには、存在する場合は独自の言語ファイルを含めることができるため、これが最善の方法だと思います。言語パラメーターは、パラメーターが互いに上書きされないように、グローバル スコープではなく特定のビューにロードする必要があります。 - データベーステーブル翻訳: これは実は私が最も心配していることです。私はできるだけ早くニュース/ページなどの翻訳を作成する方法を探しています。モジュールごとに 2 つのテーブル (たとえば、
News
とNews_translations
) を用意することも選択肢の 1 つですが、優れたシステムを得るには作業が多すぎるように感じます。私が思いついたことの 1 つは、私がdata versioning
作成したシステムに基づいています。データベース テーブル名 が 1 つあり、このテーブルには、、およびのTranslations
一意の組み合わせがあります。たとえば、en_En / News / 1 (ID=1 のニュース項目の英語版を参照) です。ただし、この方法には 2 つの大きな欠点があります。まず、データベースに大量のデータがあると、このテーブルは非常に長くなる傾向があり、次に、この設定を使用してテーブルを検索するのは大変な作業になります。たとえば、項目の SEO スラッグを検索すると、全文検索が必要になりますが、これはかなり無意味です。しかしその一方で、これはすべてのテーブルに翻訳可能なコンテンツを非常に速く作成する簡単な方法ですが、この利点が欠点を上回るとは思いません。language
tablename
primarykey
- フロントエンド作業: また、フロントエンドにも少し考慮が必要です。もちろん、利用可能な言語をデータベースに保存し、必要なものをアクティブまたは非アクティブにします。こうすることで、スクリプトは言語を選択するためのドロップダウンを生成し、バックエンドは CMS を使用してどの翻訳が可能かを自動的に決定できます。選択された言語 (例: en_EN) は、ビューの言語ファイルを取得するとき、または Web サイトのコンテンツ項目の適切な翻訳を取得するときに使用されます。
ということで、これが私のこれまでのアイデアです。日付などのローカライズ オプションはまだ含まれていませんが、私のサーバーは PHP5.3.2+ をサポートしているため、ここで説明されているように intl 拡張機能を使用するのが最善のオプションです。http://devzone.zend.com/1500/国際化-in-php-53/- しかし、これは今後の開発のどの段階でも役立つでしょう。現時点での主な問題は、Web サイトのコンテンツの翻訳のベスト プラクティスをどのように実現するかということです。
ここで説明したことの他に、まだ決めていないことがもう 1 つあります。それは単純な質問のように見えますが、実際には頭を悩ませています。
URL 翻訳? これを実行するべきか、しないべきか? また、どのような方法で実行するべきか?
では、この URL があり、http://www.domain.com/about-us
英語がデフォルトの言語である場合、http://www.domain.com/over-ons
言語としてオランダ語を選択した場合、この URL は に翻訳される必要がありますか? または、簡単な方法で、 に表示されるページのコンテンツを変更するだけです/about
。最後の方法は、同じ URL の複数のバージョンが生成されるため、有効なオプションではないようです。コンテンツのインデックス作成は、正しい方法で失敗します。
別のオプションは、代わりに を使用することですhttp://www.domain.com/nl/about-us
。これにより、各コンテンツに対して少なくとも一意の URL が生成されます。また、これにより、たとえば別の言語に移動するのが簡単になりhttp://www.domain.com/en/about-us
、提供される URL は Google と人間の訪問者の両方にとって理解しやすくなります。このオプションを使用すると、既定の言語をどうすればよいですか?既定の言語は、既定で選択された言語を削除しますか? では、リダイレクトします... 私の目http://www.domain.com/en/about-us
にhttp://www.domain.com/about-us
は、これが最善の解決策です。なぜなら、CMS が 1 つの言語のみに設定されている場合、URL にこの言語識別を含める必要がないためです。
3 つ目のオプションは、両方のオプションを組み合わせたものです。メイン言語には「言語識別なし」の URL ( http://www.domain.com/about-us
) を使用します。サブ言語には、翻訳された SEO スラッグを含む URL を使用します。http://www.domain.com/nl/over-ons
&http://www.domain.com/de/uber-uns
私の質問が皆さんの頭を悩ませてくれることを願っています。私の頭も間違いなく悩まされました。ここでの質問は、私にとってすでに物事を解決するのに役立ちました。これまで使用した方法や、今後の CMS のために考えているアイデアを見直す機会を与えてくれました。
この長い文章を読んでいただくために時間を割いていただいたことに感謝申し上げます。
// Edit #1
:
言い忘れていましたが、__() 関数は、特定の文字列を翻訳するためのエイリアスです。このメソッドには、翻訳がまだ利用できない場合にデフォルトのテキストをロードする、何らかのフォールバック メソッドが必要です。翻訳がない場合は、挿入するか、翻訳ファイルを再生成する必要があります。
ベストアンサー1
トピックの前提
多言語サイトには、次の 3 つの異なる側面があります。
- インターフェース翻訳
- コンテンツ
- URL ルーティング
これらはすべて異なる方法で相互接続されていますが、CMS の観点からは、異なる UI 要素を使用して管理され、異なる方法で保存されます。最初の 2 つについては実装と理解に自信があるようですね。質問は後者の側面についてでした。「URL 翻訳?これを行うべきか、行わないべきか?そして、どのような方法で?」
URL は何で作成できますか?
非常に重要なことは、インド代わりに賛成音訳(また、転写とローマ字化)。一見すると、IDN は国際的な URL の実行可能なオプションのように見えますが、実際には次の 2 つの理由により宣伝どおりには機能しません。
'ч'
一部のブラウザでは、またはなどの非ASCII文字を、'ž'
およびに変換します'%D1%87'
。'%C5%BE'
- ユーザーがカスタムテーマを使用している場合、テーマのフォントにはそれらの文字の記号がない可能性が非常に高いです。
実は数年前、Yii ベースのプロジェクト (ひどいフレームワークだと思います) で IDN アプローチを試しました。そのソリューションを放棄する前に、上記の両方の問題に遭遇しました。また、攻撃ベクトルになる可能性もあると疑っています。
利用可能なオプション...私が見たところ。
基本的には 2 つの選択肢があり、次のように抽象化できます。
http://site.tld/[:query]
:[:query]
言語とコンテンツの選択を決定する場所http://site.tld/[:language]/[:query]
:[:language]
URLの一部は言語の選択を定義し、[:query]
コンテンツを識別するためにのみ使用されます
クエリは Α と Ω です。
を選んだとしますhttp://site.tld/[:query]
。
[:query]
その場合、言語の主なソースが 1 つ(セグメントの内容) あり、さらに次の 2 つのソースがあります。
まず、クエリを定義されたルーティングパターンの1つに一致させる必要があります(Laravelを選択した場合は、こちらで読む)。パターンの一致が成功したら、言語を見つける必要があります。
パターンのすべてのセグメントを調べる必要があります。すべてのセグメントの潜在的な翻訳を見つけて、どの言語が使用されたかを判断します。ルーティングの競合が発生した場合 (「発生するかどうか」ではなく)、2 つの追加ソース (Cookie とヘッダー) がルーティングの競合を解決するために使用されます。
たとえば次のようになりますhttp://site.tld/blog/novinka
。
これは の音訳で"блог, новинка"
、英語ではおよそ を意味します"blog", "latest"
。
すでにお気づきかと思いますが、ロシア語では「блог」は「blog」と翻訳されます。つまり、[:query]
あなたの最初の部分(最良のシナリオ) は、可能性のある言語のリストになります['en', 'ru']
。次に、次のセグメント「novinka」を取ります。可能性のあるリストには、1 つの言語だけが含まれる可能性があります['ru']
。
リストに項目が 1 つある場合は、言語が正常に見つかります。
しかし、最終的に 2 つ (例: ロシア語とウクライナ語) 以上の可能性、または場合によっては 0 つの可能性になった場合は、正しいオプションを見つけるために Cookie やヘッダーを使用する必要があります。
それでもダメなら、サイトのデフォルト言語を選択します。
パラメータとしての言語
代わりに、 として定義できる URL を使用しますhttp://site.tld/[:language]/[:query]
。この場合、クエリを翻訳するときに言語を推測する必要はありません。その時点でどの言語を使用するかが既にわかっているからです。
言語の二次的なソースとして、Cookie 値もあります。ただし、ここでは Accept-Language ヘッダーをいじっても意味がありません。「コールド スタート」(ユーザーがカスタム クエリを使用して初めてサイトを開いたとき) の場合、考えられる言語の数が不明なため、処理する必要がないためです。
代わりに、優先順位が付けられた 3 つのシンプルなオプションがあります。
[:language]
セグメントが設定されている場合はそれを使用する- 設定されている場合
$_COOKIE['lang']
、それを使用する - デフォルトの言語を使用する
言語がわかったら、クエリの翻訳を試み、翻訳が失敗した場合は、その特定のセグメントの「デフォルト値」を使用します (ルーティング結果に基づきます)。
ここに第3の選択肢はないでしょうか?
はい、技術的には両方のアプローチを組み合わせることができますが、プロセスが複雑になり、URL を手動で変更してニュース ページがドイツ語に変更されることを期待しているユーザーのみに対応しhttp://site.tld/en/news
ますhttp://site.tld/de/news
。
しかし、この場合でも、Cookie 値 (以前の言語選択に関する情報が含まれます) を使用することで、魔法や希望を少なくして実装することで、おそらく軽減される可能性があります。
どのアプローチを使用すればよいですか?
すでにご想像のとおり、http://site.tld/[:language]/[:query]
より賢明な選択肢として をお勧めします。
また、現実世界では、URL の 3 番目の主要部分は「タイトル」になります。オンライン ショップの製品名やニュース サイトの記事の見出しなどがこれにあたります。
例:http://site.tld/en/news/article/121415/EU-as-global-reserve-currency
この場合は'/news/article/121415'
がクエリで、 が'EU-as-global-reserve-currency'
タイトルになります。純粋に SEO 目的です。
Laravel でできますか?
そうですね、でもデフォルトではありません。
私はあまり詳しくないのですが、私が見た限りでは、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 );
.. または、特定の実装に応じて、そのバリエーションになります。