Qtブログ(日本語)

Qt LinguistにおけるIDベースの翻訳のラベルによる整理

作成者: Qt Group 日本オフィス|Dec 25, 2025 8:08:09 AM
このブログは「Organizing ID-Based Translations with Labels in Qt Linguist」の抄訳です。

大規模なQtアプリケーションの翻訳は、特にIDベースの翻訳を使用する場合に困難を伴います。テキストベースの翻訳はコンテキスト(翻訳が行われるクラスや名前空間)ごとに整理されるのに対し、IDベースの翻訳には従来、この組織構造が欠けていました。数百から数千ものIDベースの翻訳文字列を含む大規模プロジェクトでは、翻訳者は「<unnamed context>」という単一の膨大なリストを操作せざるを得ませんでした。

この課題に対処する新機能「IDベース翻訳用ラベル」をQt 6.11で提供開始します。ラベルはIDベース翻訳エントリを論理的なグループに整理するシンプルかつ強力な手段であり、翻訳者のワークフロー効率化を実現します。

課題 - IDベースの翻訳の整理

Qt は国際化 (i18n) に対して2つのアプローチを提供します。

テキストベースの翻訳では、翻訳を検索するキーとしてソーステキストとコンテキストの組み合わせを使用します。コンテキストは、コード構造の中で翻訳が発生する場所を特定します。C++では、クラス内で tr("Open File") を呼び出すとき、コンテキストはクラス名とその名前空間です。QMLでは、qsTr("Open File") 呼び出すと、コンテキストは通常QMLファイル名、またはpragma Translator の記法を使って定義した場合はカスタマイズしたコンテキスト名となります。

翻訳システムは原文("Open File")とコンテキストをルックアップのキーとして使用します。これらの翻訳は自動的に整理され、Qt Linguist のコンテキストごとにグループ化されるため、翻訳者は各文字列がアプリケーションのどの部分に属しているかを簡単に理解することができます。

IDベースの翻訳では、ソーステキストの代わりに一意の識別子を使用します。C++ で qtTrId("msg.open")、QML で qsTrId("msg.open") を呼び出すと、翻訳システ ムはルックアップ・キーとして "msg.open "を使用します。この方法にはいくつかの利点があります:

  • ソース・テキストが変更されても ID は不変
  • IDが短いため、ファイルサイズが小さくなる
  • 同じIDとその翻訳を異なるクラスや名前空間で再利用可能
  • 後期の国際化に適している
  • UIに表示されるものをコードから分離し、UIテキストを独立して管理できる

しかし、IDベースの翻訳には固有のコンテキストがありません。 翻訳者がQt LinguistでIDベースの翻訳を含むTSファイルを開くと、すべてのエントリが1つのグループに表示され、ナビゲートや翻訳作業の構成を理解するのが難しくなります。

ラベル利用前:すべてのIDベースの翻訳が<unnamed context>の下に表示

解決策 - ラベルの導入

ラベルは、アプリケーションの実行時の動作に影響を与えることなく、IDベースの翻訳を意味のあるグループに分類する方法を提供します。テキストベースの翻訳がコンテキストによってグループ化されるように、IDベースの翻訳もラベルによってグループ化することができます。それでも、翻訳のルックアップは以前と同じようにIDだけを使用して実行されます。ラベルは、翻訳者が翻訳作業をナビゲートし、構造を理解するための、純粋に組織的な目的のためのものです。

ラベルの仕組み

IDベースの翻訳にラベルを追加するのは簡単です。翻訳の前に
//@ LabelName コメントを追加するだけです。

C++の例

//% "Open file"
//@ FileOperations
qtTrId("msg.open");

//% "Save file"
//@ FileOperations
qtTrId("msg.save");

//% "Connection timeout"
//@ NetworkErrors
qtTrId("err.timeout");

QMLの例

//% "Open file"
//@ FileOperations
qsTrId("msg.open")

//% "Save file"
//@ FileOperations
qsTrId("msg.save")

//% "Connection timeout"
//@ NetworkErrors
qsTrId("err.timeout")

lupdateがこれらの翻訳を抽出するとき、IDベースの各エントリーにラベルを関連付けます。Qt Linguistでは、同じラベルを持つエントリは一緒にグループ化されます(コンテキストでグループ化されたテキストベースの翻訳に似ています)。

ラベル利用後:翻訳文字列がラベルによって整理されて表示

異なるファイルタイプでのラベル

ラベルは、ID ベースの翻訳をサポートするすべての Qt ファイルタイプで一貫して機能します。

  • C++ ソースファイル: qtTrId()呼び出しの前に //@ LabelNameコメントを使用。
  • QMLファイル: qsTrId()呼び出しの前に //@ LabelNameコメントを使用。
  • Qt Widgets Designer UIファイル: フォーム設定でラベルを設定すると、そのフォーム内のIDベースの翻訳はすべてそのラベルを継承します。

PythonのQtはIDベースの翻訳をサポートしていないので、Pythonのファイルには同等のものがないことに注意してください。

ラベルフィールドを表示する Qt Widgets Designer のフォーム設定

ラベルの自動生成

手動でラベル名を指定するのは多くのプロジェクトでうまくいきますが、大規模なコードベース全体で一貫したラベル名を維持するのは面倒になることがあります。コード構造からラベルを派生させるプレースホルダを使用することで、ラベルを自動生成することができます。

利用可能なプレースホルダー

  • <context> - 完全なコンテキストパス(C++ではnamespace::class、QMLではコンポーネント名)を使用します。
  • <class> - 名前空間プレフィックスを除いたクラス名のみを使用します(C++のみ)。
  • <file> - ソースファイル名を使用します。

例えば、C++の場合

namespace MyApp {
class FileHandler : QObject {
    Q_OBJECT
    void open()
{
        //% "Open file"
        //@ <context>
        qtTrId("msg.open");  // Label becomes: MyApp::FileHandler

        //% "Save file"
        //@ <class>
        qtTrId("msg.save");  // Label becomes: FileHandler

        //% "Export"
        //@ <file>
        qtTrId("msg.export");  // Label becomes: filehandler.cpp
    }
};
}

またはQMLでは

// main.qml
Item {
    id: mainView
    Component.onCompleted: {
        //% "Loading"
        //@ <context>
        qsTrId("msg.loading")  // Label becomes: mainView

        //% "Ready"
        //@ <file>
        qsTrId("msg.ready")  // Label becomes: main.qml
    }
}

プレースホルダーの組み合わせ

プレースホルダーはカスタムテキストと組み合わせて、より明確なラベルを作成することができます。

//% "Open file"
//@ <file>:<class>
qtTrId("msg.open");  // Label: filehandler.cpp:FileHandler

//% "Network error"
//@ module_<context>
qtTrId("err.network");  // Label: module_MyApp::ErrorHandler

//% "Status update"
//@ <file>_<class>-state
qtTrId("status.update");  // Label: statusbar.cpp_StatusBar-state

 

自動ラベルでIDベースの翻訳を表示
 

自動ラベルは、手動でのラベル管理が現実的でない大規模なプロジェクトで特に役立ちます。開発者がラベル名を手動で管理することなく、コード構造に基づいた一貫性のあるグループ化を保証し、各翻訳がどこで使用されているかについてのコンテキストヒントを翻訳者に自動的に提供します。

重要な注意事項
  • 自動ラベルは ID ベースの翻訳(qtTrId,qsTrId) でのみ機能します。
  • QML 内や C++ クラスの外部で <class> を使用すると警告が表示されますので、代わりに <unnamed> が表示されます。

Qt Linguist インターフェイスの強化

新しいラベル機能をサポートするために、Qt Linguist は混合された翻訳タイプを扱うための、より直感的なインターフェイスに強化されました。コンテキストドックウィジェットには、翻訳者が切り替えられるタブが追加されました:

  • テキストベースの翻訳:コンテキスト(クラス/名前空間)ごとに整理
  • IDベースの翻訳:ラベルで整理

この分離により、一度に 1 つの翻訳アプローチに集中しやすくなりました。

 

新しいタブ付きインターフェイスの「Text Based」と「ID Based」タブ
 

ID ベースの翻訳では、列のヘッダーに関連情報が表示されます。

  • ラベル:翻訳のグループ種別
  • ID:翻訳の識別子(例:"msg.open")
  • ソーステキスト: //% "text" コメントからの文字列

ラベルのないエントリは、"<unnamed label>"の下に表示されます。

重要な考慮事項

ラベルは整理専用

ラベルは純粋に整理のためのものです。翻訳者が翻訳作業の構造を理解し、作業を進めるための補助として存在します。実行時の動作には一切影響しません

  • IDはすべてのラベルで一意です。
  • ラベルを参照せずに qtTrId("msg.open")を呼び出します。
  • 実行時の翻訳システムはIDだけで翻訳を検索します。
  • ラベルを変更しても、翻訳の読み取り方法には影響しません。

これにより、アプリケーションの機能に影響を与えることなく、ラベルを追加、変更、削除することができます。

ラベルの検証

lupdateは、ラベルを正しく使うための検証を行います。

無効:テキストベースの翻訳のラベル

//@ MyLabel        // Warning: labels cannot be used with text-based translation
tr("Open File");

警告:同じIDのラベルの衝突

//% "Open file"
//@ FileOperations
qtTrId("msg.open");

// コードの後半で...
//% "Open file"
//@ UserInterface   // Warning: contradicting labels for id "msg.open"
qtTrId("msg.open");

同じIDが異なるラベルで利用されると、lupdateは警告を発します。

コードベースでは、特定のIDのラベルを一度定義すれば十分です。しかし、コードをわかりやすくするために、複数の場所でラベルを繰り返すこともできます。ラベルは、最初に定義された場所に関係なく、翻訳ファイルのIDに関連付けられます。

実例 - 大規模なアプリケーションの整理

ラベルが実際のアプリケーションの翻訳ワークフローをどのように改善するか見てみましょう。数百のIDベースの翻訳を持つ医療機器のインターフェイスを考えてみましょう。

ラベルを使う前は、500以上の翻訳がすべて「<unnamed context>」という単一のリストに表示されていました。翻訳者は、どの文字列がどの機能に属するかを理解するのが難しく、文脈の混乱や翻訳の遅れにつながっていました。

ラベルを使用すると、翻訳は論理的なグループに整理されます。便利なパターンは、QT_TRID_NOOP を使用して、すべての ID ベースの翻訳をソーステキストとラベルとともに専用の場所(ヘッダーファイルなど)に一度定義することです。

// translations.h - Central definition of all ID-based translations

// Patient Management
//% "Patient Name"
//@ PatientManagement
QT_TRID_NOOP("patient.name");

//% "Date of Birth"
//@ PatientManagement
QT_TRID_NOOP("patient.dob");

//% "Patient ID"
//@ PatientManagement
QT_TRID_NOOP("patient.id");

// Vital Signs Monitoring
//% "Heart Rate"
//@ VitalSigns
QT_TRID_NOOP("vitals.heartrate");

//% "Blood Pressure"
//@ VitalSigns
QT_TRID_NOOP("vitals.bloodpressure");

//% "Temperature"
//@ VitalSigns
QT_TRID_NOOP("vitals.temperature");

// Device Errors
//% "Sensor Disconnected"
//@ DeviceErrors
QT_TRID_NOOP("err.sensor.disconnected");

//% "Battery Low"
//@ DeviceErrors
QT_TRID_NOOP("err.battery.low");

//% "Calibration Required"
//@ DeviceErrors
QT_TRID_NOOP("err.calibration.required");

一度一元的に定義すれば、ソース文字列やラベルを繰り返すことなく、コードベース全体でこれらのIDを使用することができます。

// patientview.cpp
void PatientView::displayPatient(const Patient &patient)
{
    nameLabel->setText(qtTrId("patient.name"));
    dobLabel->setText(qtTrId("patient.dob"));
    idLabel->setText(qtTrId("patient.id"));
}

// vitalsmonitor.cpp
void VitalsMonitor::updateDisplay()
{
    heartRateLabel->setText(qtTrId("vitals.heartrate"));
    bpLabel->setText(qtTrId("vitals.bloodpressure"));
    tempLabel->setText(qtTrId("vitals.temperature"));
}

// errorhandler.cpp
void ErrorHandler::showError(ErrorType type)
{
    QString message;
    if (type == SensorError)
        message = qtTrId("err.sensor.disconnected");
    else if (type == BatteryError)
        message = qtTrId("err.battery.low");
    // ...
}

これにより、翻訳者は論理的なセクションごとに作業を進められるようになります。まず患者管理関連の文字列をまとめて翻訳し、次にバイタルサイン関連の文字列をまとめて翻訳するといった具合です。グループ化された構成により文脈が明確になり、翻訳作業がより直感的で効率的になります。この手法は、クリーンで読みやすいコードを維持するとともに、全ての翻訳定義を管理しやすい一箇所に集約します。

ラベル使用のベストプラクティス

当社の実装とテストに基づき、効果的なラベル使用に関する推奨事項を以下にご紹介します。

  • 明確な名称を使用し、機能領域や特徴を反映(例:FileOperations、NetworkErrors、UserAuthentication)。
  • 翻訳者が各文字列の文脈と目的を理解しやすくするために機能単位で整理
  • 大規模プロジェクト向けに階層的な命名規則を採用(例:Settings.Display、Settings.Audio、Settings.Network)。
  • プロジェクト全体を通じて命名規則の一貫性を維持
  • チーム向けのガイドラインと共に、特に階層的またはドメイン固有の命名規則について、設計を文書化

採用と互換性

ラベルは Qt 6.11 から利用できます。プロジェクトで使用するには

  1. Qt 6.11 以降にアップデートします
  2. LabelNameまたは//@ <placeholder> を使用して、ID ベースの翻訳にラベルコメントを追加します
  3. lupdate を実行して、ラベル情報を含む翻訳を抽出します
  4. Qt Linguist で TS ファイルを開き、整理されたグループを確認します

Qt Widgets Designer UI ファイルの場合、フォーム設定ダイアログでラベルを設定すると、そのフォーム内のすべての ID ベースの翻訳が自動的にラベルを継承します。

ラベルは既存のプロジェクトに統合できます。ラベルのないTSファイルは、Linguistの「<unnamed label>」の下にラベルのないエントリが表示され、完全に機能し続けます。一度にすべての翻訳にラベルを付ける必要はなく、少しずつラベルを追加することができます。旧バージョンの Qt を使用しているユーザーのために、最小限の後方互換性サポートにより、ラベル付きの UI ファイルが旧バージョンでエラーを生成しないようにします。

技術的な実装の詳細

実装に興味のある方のために、ラベルは以下の通りです。

  • C++ および QML パーサー内の//@ LabelNameコメントからlupdate によって抽出されます
  • 翻訳エントリのXML属性としてTSファイルに保存されます
  • テキストベースとIDベースの翻訳に別々のモデルを持つ拡張UIでLinguistで表示されます
  • 翻訳ロードシステムにより、実行時に無視されます
  • lupdate パーサー(C++、QML、UI ファイル)、翻訳ファイルフォーマット(TS XML スキーマ)、Qt Linguist UI、Qt ウィジェットデザイナー(フォーム設定のラベルフィールド)など、複数のコンポーネントにわたって実装されています

まとめ

ラベルは Qt における ID ベースの翻訳に整理をもたらし、大規模なプロジェクトに携わる翻訳者の課題に対処します。関連する翻訳をグループ化するためのシンプルな構文を提供することで、ラベル名が手動割り当てであれ、コード構造からの自動生成であれ、ラベルは翻訳ワークフローをより効率的で直感的なものにします。

新しいプロジェクトを開始する場合でも、既存のプロジェクトを保守する場合でも、ラベルを使用することで、国際化ワークフローを低労力で効果的に改善することができます。この機能は既存の Qt 翻訳ツールとよく統合でき、開発プロセスへの変更も最小限で済みます。ラベルは Qt 6.11 で利用可能になります。

Qt 6.11 以降のプロジェクトでラベルを試し、フィードバックを Qt コミュニティと共有することをお勧めします。Qt の国際化機能の詳細については、Qt Linguist Manual を参照してください。