From 6f6b9078fab328ba592822484426e11272d544b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=A8=E3=82=93=E3=81=A8=E3=82=93=E3=81=BC?= <70003919+KaitoMuraoka@users.noreply.github.com> Date: Mon, 9 Sep 2024 18:47:49 +0900 Subject: [PATCH] Translate Leaf to Japanese (#1005) [Leaf content](https://docs.vapor.codes/ja/leaf/overview/) translated into Japanese - getting-started.md - overview.md - custom-tags.md --------- Co-authored-by: Tim Condon <0xTim@users.noreply.github.com> --- docs/leaf/custom-tags.ja.md | 128 ++++++++++++++ docs/leaf/getting-started.ja.md | 95 +++++++++++ docs/leaf/overview.ja.md | 291 ++++++++++++++++++++++++++++++++ 3 files changed, 514 insertions(+) create mode 100644 docs/leaf/custom-tags.ja.md create mode 100644 docs/leaf/getting-started.ja.md create mode 100644 docs/leaf/overview.ja.md diff --git a/docs/leaf/custom-tags.ja.md b/docs/leaf/custom-tags.ja.md new file mode 100644 index 00000000..b2c0f1c6 --- /dev/null +++ b/docs/leaf/custom-tags.ja.md @@ -0,0 +1,128 @@ +# カスタムタグ + +[`LeafTag`](https://api.vapor.codes/leafkit/documentation/leafkit/leaftag) プロトコルを使用して、カスタム Leaf タグを作成することができます。 + +これを実際に試してみるために、現在のタイムスタンプを表示するカスタムタグ `#now` を作成してみましょう。このタグは、日付形式を指定するためのオプションのパラメータもサポートします。 + +!!! tip + もしカスタムタグが HTML をレンダリングする場合は、HTML がエスケープされないように、`UnsafeUnescapedLeafTag` に準拠させる必要があります。ユーザー入力のチェックやサニタイズを忘れないようにしましょう。 + +## `LeafTag` + +まず、`NowTag` というクラスを作成し、`LeafTag` に準拠させます。 + +```swift +struct NowTag: LeafTag { + func render(_ ctx: LeafContext) throws -> LeafData { + ... + } +} +``` + +次に、`render(_:)` メソッドを実装します。このメソッドに渡される `LeafContext` は、必要なすべての情報を持っています。 + +```swift +enum NowTagError: Error { + case invalidFormatParameter + case tooManyParameters +} + +struct NowTag: LeafTag { + func render(_ ctx: LeafContext) throws -> LeafData { + let formatter = DateFormatter() + switch ctx.parameters.count { + case 0: formatter.dateFormat = "yyyy-MM-dd HH:mm:ss" + case 1: + guard let string = ctx.parameters[0].string else { + throw NowTagError.invalidFormatParameter + } + + formatter.dateFormat = string + default: + throw NowTagError.tooManyParameters + } + + let dateAsString = formatter.string(from: Date()) + return LeafData.string(dateAsString) + } +} +``` + +## タグの設定 + +`NowTag` を実装したので、Leaf にそれを伝えるだけです。このようにして、たとえ別のパッケージで定義されたタグでも追加することができます。通常、これを `configure.swift` で行います: + +```swift +app.leaf.tags["now"] = NowTag() +``` + +これで完了です!Leaf でカスタムタグを使用できるようになりました。 + +```leaf +The time is #now() +``` + +## コンテキストプロパティ + +`LeafContext` には、重要なプロパティが 2 つあります。それが `parameters` と `data` です。この 2 つで必要な情報はすべて揃っています。 + +- `parameters`: タグのパラメータを含む配列です +- `data`: コンテキストとして `render(_:_:)` に渡されたビューのデータを含む辞書です + +### Hello タグによる実例 + +これを理解するために、両方のプロパティを使ったシンプルな hello タグを実装してみましょう。 + +#### parameters の使用 + +nameの値が提供される、1つ目のパラメータにアクセスできます + +```swift +enum HelloTagError: Error { + case missingNameParameter +} + +struct HelloTag: UnsafeUnescapedLeafTag { + func render(_ ctx: LeafContext) throws -> LeafData { + guard let name = ctx.parameters[0].string else { + throw HelloTagError.missingNameParameter + } + + return LeafData.string("

Hello \(name)

") + } +} +``` + +```leaf +#hello("John") +``` + +#### data の使用 + +data プロパティの中の "name" キーを使って名前の値にアクセスします。 + +```swift +enum HelloTagError: Error { + case nameNotFound +} + +struct HelloTag: UnsafeUnescapedLeafTag { + func render(_ ctx: LeafContext) throws -> LeafData { + guard let name = ctx.data["name"]?.string else { + throw HelloTagError.nameNotFound + } + + return LeafData.string("

Hello \(name)

") + } +} +``` + +```leaf +#hello() +``` + +_Controller_: + +```swift +return try await req.view.render("home", ["name": "John"]) +``` diff --git a/docs/leaf/getting-started.ja.md b/docs/leaf/getting-started.ja.md new file mode 100644 index 00000000..85e8b369 --- /dev/null +++ b/docs/leaf/getting-started.ja.md @@ -0,0 +1,95 @@ +# Leaf + +Leaf は、Swift にインスパイアされた構文を持つ強力なテンプレート言語です。これを使って、フロントエンドのウェブサイト向けに動的な HTML ページを生成したり、API から送信するリッチなメールを生成したりできます。 + +## Package + +Leaf を使用する最初のステップは、プロジェクトの SPM パッケージマニフェストファイルに依存関係として追加することです。 + +```swift +// swift-tools-version:5.8 +import PackageDescription + +let package = Package( + name: "MyApp", + platforms: [ + .macOS(.v10_15) + ], + dependencies: [ + /// Any other dependencies ... + .package(url: "https://github.com/vapor/leaf.git", from: "4.4.0"), + ], + targets: [ + .target(name: "App", dependencies: [ + .product(name: "Leaf", package: "leaf"), + // Any other dependencies + ]), + // Other targets + ] +) +``` + +## 設定 + +パッケージをプロジェクトに追加したら、Vapor を設定してそれを使用するように構成します。これは通常、[`configure.swift`](../getting-started/folder-structure.md#configureswift) で行います。 + +```swift +import Leaf + +app.views.use(.leaf) +``` + +これにより、コード内で `req.view` を呼び出すと、Vapor が `LeafRenderer` を使用するように指示します。 + +!!! note + Leaf には、ページをレンダリングするための内部キャッシュがあります。`Application` の環境が `.development` に設定されている場合、このキャッシュは無効になり、テンプレートへの変更が即座に反映されます。`.production` やその他の環境では、キャッシュがデフォルトで有効になっており、テンプレートに加えた変更はアプリケーションを再起動するまで反映されません。 + +!!! warning + Xcode から実行する際に Leaf がテンプレートを見つけられるようにするためには、 Xcode ワークスペースの [custom working directory](../getting-started/xcode.md#_1) を設定する必要があります。 +## フォルダ構成 + +Leaf を設定したら、`.leaf` ファイルを格納するための `Views` フォルダを用意する必要があります。デフォルトでは、Leaf はプロジェクトのルートに対して `./Resources/Views` というフォルダを要求します。 + +JavaScript や CSS ファイルを提供する予定がある場合は、Vapor の [`FileMiddleware`](https://api.vapor.codes/vapor/documentation/vapor/filemiddleware) を有効にして、 `/Public` フォルダからファイルを提供できるようにすることも可能です。 + +``` +VaporApp +├── Package.swift +├── Resources +│   ├── Views +│   │   └── hello.leaf +├── Public +│   ├── images (images resources) +│   ├── styles (css resources) +└── Sources +    └── ... +``` + +## Viewのレンダリング + +Leaf が設定できたので、最初のテンプレートをレンダリングしてみましょう。`Resources/Views` フォルダ内に、次の内容で `hello.leaf` という新しいファイルを作成します。 + +```leaf +Hello, #(name)! +``` + +!!! tip + もし、コードエディタとして VSCode を使用している場合、シンタックスハイライトを有効にするために、Leaf 拡張機能をインストールすることをお勧めします:[Leaf HTML](https://marketplace.visualstudio.com/items?itemName=Francisco.html-leaf) + +次に、View をレンダリングするルートを登録します。(通常は、`routes.swift` やコントローラで行います) + +```swift +app.get("hello") { req -> EventLoopFuture in + return req.view.render("hello", ["name": "Leaf"]) +} + +// or + +app.get("hello") { req async throws -> View in + return try await req.view.render("hello", ["name": "Leaf"]) +} +``` + +ここでは、Leaf を直接呼び出すのではなく、`Request` の汎用 `view` プロパティを使用します。これにより、テスト時に別のレンダラーに切り替えることができます。 + +ブラウザを開き、`/hello` にアクセスしてください。 `Hello, Leaf!` と表示されているはずです。これで最初の Leaf ビューのレンダリングが完了です! diff --git a/docs/leaf/overview.ja.md b/docs/leaf/overview.ja.md new file mode 100644 index 00000000..1f718494 --- /dev/null +++ b/docs/leaf/overview.ja.md @@ -0,0 +1,291 @@ +# Leaf 概要 + +Leaf は、Swift にインスパイアされた構文を持つ強力なテンプレート言語です。これを使って、フロントエンドのウェブサイト向けに動的な HTML ページを生成したり、API から送信するリッチなメールを生成したりできます。 + +このガイドでは、Leaf の構文と使用可能なタグについての概要を説明します。 + +## テンプレート構文 + +ここに、基本的な Leaf タグの使用例を示します。 + +```leaf +There are #count(users) users. +``` + +Leaf タグは、以下の 4 つの要素で構成されています: + +- トークン `#` : Leaf パーサーがタグの開始を検出するためのシグナルです +- 名前 `count` : タグを識別するための名前です +- パラメータリスト `(users)` : 0個以上の引数を受け取ることができます +- ボディ:一部のタグには、コロンと閉じタグを使用して任意のボディを追加できるタグもあります + +これらの4つの要素は、タグの実装に応じて様々な使い方ができます。 Leaf の標準タグがどのように使われるか、いくつかの例をみてみましょう。 + +```leaf +#(variable) +#extend("template"): I'm added to a base template! #endextend +#export("title"): Welcome to Vapor #endexport +#import("body") +#count(friends) +#for(friend in friends):
  • #(friend.name)
  • #endfor +``` + +Leaf は、Swift でお馴染みの多くの式もサポートしています。 + +- `+` +- `%` +- `>` +- `==` +- `||` +- etc. + +```leaf +#if(1 + 1 == 2): + Hello! +#endif + +#if(index % 2 == 0): + This is even index. +#else: + This is odd index. +#endif +``` + +## Context + +[Getting Started](getting-started.ja.md) の例では、 `[String: String]` 辞書を使って Leaf にデータを渡しましたが、`Encodable` に準拠する任意のデータを渡すことができます。実際、`[String: Any]` はサポートされていないため、`Encodable` な構造体を使用する方が推奨されます。つまり、配列を直接渡すことは*できず*、代わりに構造体にラップする必要があります: + +```swift +struct WelcomeContext: Encodable { + var title: String + var numbers: [Int] +} +return req.view.render("home", WelcomeContext(title: "Hello!", numbers: [42, 9001])) +``` + +これにより、`title` と `numbers` が Leaf テンプレートに公開され、タグ内で使用できるようになります。例: + +```leaf +

    #(title)

    +#for(number in numbers): +

    #(number)

    +#endfor +``` + +## 使用例 + +以下は、Leaf の一般的な使用例です。 + +### 条件 + +Leaf は、`#if` タグを使用してさまざまな条件を評価できます。例えば、変数を提供すると、その変数がコンテキストに存在するかチェックします: + +```leaf +#if(title): + The title is #(title) +#else: + No title was provided. +#endif +``` + +また、比較を行うこともできます。例: + +```leaf +#if(title == "Welcome"): + This is a friendly web page. +#else: + No strangers allowed! +#endif +``` + +別のタグを条件の一部として使用したい場合は、内側のタグの `#` を省略する必要があります。例: + +```leaf +#if(count(users) > 0): + You have users! +#else: + There are no users yet :( +#endif +``` + +また、`#elseif` ステートメントを使用することもできます: + +```leaf +#if(title == "Welcome"): + Hello new user! +#elseif(title == "Welcome back!"): + Hello old user +#else: + Unexpected page! +#endif +``` + +### ループ + +アイテムの配列を提供すると、Leaf はそれをループし、 `#for` タグを使用して各アイテムを個別に操作できます。 + +例えば、Swift コードを更新して、惑星のリストを提供することができます: + +```swift +struct SolarSystem: Codable { + let planets = ["Venus", "Earth", "Mars"] +} + +return req.view.render("solarSystem", SolarSystem()) +``` + +その後、Leaf で次のようにループを行うことができます: + +```leaf +Planets: + +``` + +これにより、次のようなビューがレンダリングされます: + +``` +Planets: +- Venus +- Earth +- Mars +``` + +### テンプレートの拡張 + +Leaf の `#extend` タグを使用すると、あるテンプレートの内容を別のテンプレートにコピーすることができます。このタグを使用する場合、テンプレートファイルの拡張子 .leaf を省略することが奨励されます。 + +拡張は、複数のページで共有される標準的なコンテンツ、例えばフッターや広告コード、テーブルなどをコピーするのに便利です: + +```leaf +#extend("footer") +``` + +このタグは、あるテンプレートを別のテンプレートの上に構築する場合にも便利です。例えば、レイアウト用の layout.leaf ファイルを作成し、ウェブサイトの HTML 構造、CSS、JavaScript など、ページごとに異なるコンテンツを配置する場所を開けておくことができます。 + +この方法を使用すると、独自のコンテンツを入力し、それを適切に配置する親テンプレートを拡張する子テンプレートを構築することができます。このため、`#export` および `#import` タグを使用して、コンテンツをコンテキストに保存し、後でそれを取得することができます。 + +例えば、次のような `child.leaf` テンプレートを作成できます: + +```leaf +#extend("master"): + #export("body"): +

    Welcome to Vapor!

    + #endexport +#endextend +``` + +ここでは、`#export` を使用して HTML を保存し、現在拡張しているテンプレートで利用できるようにしています。その後、`master.leaf` をレンダリングし、Swift から渡された他のコンテキスト変数と共にエクスポートされたデータを使用します。例えば、`master.leaf` は次のようになります: + +```leaf + + + #(title) + + #import("body") + +``` + +ここでは、`#import` を使用して、`#extend` タグに渡されたコンテンツを取得しています。Swift から `["title": "Hi there!"]` が渡されると、`child.leaf` は次のようにレンダリングされます: + +```html + + + Hi there! + +

    Welcome to Vapor!

    + +``` + +### その他のタグ + +#### `#count` + +`#count` タグは、配列内のアイテム数を返します。例: + +```leaf +Your search matched #count(matches) pages. +``` + +#### `#lowercased` + +`#lowercased` タグは、文字列内のすべての文字を小文字に変換します。 + +```leaf +#lowercased(name) +``` + +#### `#uppercased` + +`#uppercased` タグは、文字列内のすべての文字を大文字に変換します。 + +```leaf +#uppercased(name) +``` + +#### `#capitalized` + +`#capitalized` タグは、文字列の各単語の最初の文字を大文字にし、他の文字を小文字に変換します。詳細は [`String.capitalized`](https://developer.apple.com/documentation/foundation/nsstring/1416784-capitalized)をご覧ください。 + +```leaf +#capitalized(name) +``` + +#### `#contains` + +`#contains` タグは、配列と値を2つのパラメータとして受け取り、パラメータ1の配列にパラメータ2の値が含まれているかどうかを返します。 + +```leaf +#if(contains(planets, "Earth")): + Earth is here! +#else: + Earth is not in this array. +#endif +``` + +#### `#date` + +`#date` タグは、日付を読みやすい文字列にフォーマットします。デフォルトでは ISO8601 フォーマットを使用します。 + +```swift +render(..., ["now": Date()]) +``` + +```leaf +The time is #date(now) +``` + +カスタム日付フォーマット文字列を2番目の引数として渡すこともできます。詳細は Swift の [`DateFormatter`](https://developer.apple.com/documentation/foundation/dateformatter) をご覧ください。 + +```leaf +The date is #date(now, "yyyy-MM-dd") +``` + +日付フォーマッターのタイムゾーン ID を3番目の引数として渡すこともできます。詳細は Swift の [`DateFormatter.timeZone`](https://developer.apple.com/documentation/foundation/dateformatter/1411406-timezone) または [`TimeZone`](https://developer.apple.com/documentation/foundation/timezone) をご覧ください。 + +```leaf +The date is #date(now, "yyyy-MM-dd", "America/New_York") +``` + +#### `#unsafeHTML` + +`#unsafeHTML` タグは、変数タグ(例: `#(variable)`) のように動作します。しかし、`variable` が含む可能性のある HTML はエスケープしません。 + +```leaf +The time is #unsafeHTML(styledTitle) +``` + +!!! note + このタグを使用する際は、提供する変数がユーザーを XSS 攻撃に晒さないように注意する必要があります。 + +#### `#dumpContext` + +`dumpContext` タグは、コンテキスト全体を人間が読める形式でレンダリングします。このタグを使用して、現在のレンダリングに提供されているコンテキストをデバッグします。 + +```leaf +Hello, world! +#dumpContext +``` \ No newline at end of file