Skip to content

[feat] 物品一覧ページの英語対応、英語名の編集機能を作成しました。また、DeepLによる自動翻訳機能も追加しました#2037

Open
haruto-kamijo wants to merge 5 commits intogm3/developfrom
feat/harujo/add-en-rental-item-list
Open

[feat] 物品一覧ページの英語対応、英語名の編集機能を作成しました。また、DeepLによる自動翻訳機能も追加しました#2037
haruto-kamijo wants to merge 5 commits intogm3/developfrom
feat/harujo/add-en-rental-item-list

Conversation

@haruto-kamijo
Copy link
Copy Markdown
Contributor

@haruto-kamijo haruto-kamijo commented Apr 30, 2026


対応Issue

resolve #2024

概要

  • 物品(rental_items)に英語名(name_en)を追加し、一覧・詳細・追加・編集画面で管理できるようにした
  • DeepL APIを使った自動翻訳機能を実装し、物品名から英語名を自動生成できるようにした

実装詳細

  • admin_view/nuxt-project/pages/rental_items/index.vue
    • 一覧テーブルに英語名カラムを追加
    • 追加モーダルに英語名入力フィールドと自動翻訳ボタンを追加
  • admin_view/nuxt-project/pages/rental_items/_id.vue
    • 詳細テーブルに英語名を表示
    • 編集モーダルに英語名入力フィールドと自動翻訳ボタンを追加
  • api/app/controllers/rental_items_controller.rb
    • create / update 時に name_en が空の場合、DeepL で自動翻訳して保存
    • POST /rental_items/translate エンドポイントを追加(フロントからの翻訳リクエスト用)
  • api/app/controllers/application_controller.rb
    • translate_to_en メソッドを追加(translate_to_ja と対称な実装)
    • 翻訳結果を7日間キャッシュ、APIキー未設定・エラー時は元テキストを返すフォールバック付き
  • api/config/routes.rb
    • POST /rental_items/translate のルートを追加
  • compose.yml
    • APIコンテナに api/.env を読み込む env_file 設定を追加(DeepL APIキーの受け渡し)

画面スクリーンショット等

image image image

テスト項目

  • 物品一覧に英語名カラムが表示されているか
  • 追加モーダルで英語名を手動入力して登録できるか
  • 追加モーダルで自動翻訳ボタンを押すと英語名が自動入力されるか
  • 編集モーダルで既存の英語名が表示され、編集・保存できるか
  • 編集モーダルで自動翻訳ボタンを押すと英語名が上書きされるか
  • 英語名が空のまま登録した場合、DeepLで自動翻訳されて保存されるか
  • 詳細ページに英語名が表示されているか

備考

  • DeepL APIキーは api/.envDEEPL_API_KEY として設定が必要(Free プランの場合キー末尾が :fx
  • compose.ymlapi/.envenv_file 指定を追加しているため、既存の webhook.env との併用となっている
  • 翻訳結果はRailsキャッシュに7日間保存されるため、同一テキストの再翻訳はAPIを呼ばずキャッシュから返される

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 30, 2026

📝 Walkthrough

Walkthrough

Adds admin-side English name support for rental items: UI shows/edits name_en; frontend can auto-translate via a new API. Backend integrates DeepL (via deepl-rb), caches translations, auto-populates name_en on create/update, and exposes POST /rental_items/translate.

Changes

Rental items English-name feature

Layer / File(s) Summary
Routing / Env
api/config/routes.rb, compose.yml
Adds POST /rental_items/translate collection route; includes ./api/.env in api service env_file.
Dependency
api/Gemfile
Adds deepl-rb gem dependency.
Translation Core (backend)
api/app/controllers/application_controller.rb
Implements private DeepL helpers: translate_to_en, translate_to_ja, request_deepl_translation, deepl_api_key, deepl_cache_key, translatable_english_text? with SHA-256 cache (7 days) and error handling.
API Controller Wiring
api/app/controllers/rental_items_controller.rb
create/update now set params[:name_en] = translate_to_en(params[:name]) when name_en is blank; new translate action returns { name_en: translated }.
Frontend UI / Behavior
admin_view/nuxt-project/pages/rental_items/index.vue, admin_view/nuxt-project/pages/rental_items/_id.vue
Adds nameEn and isTranslating data, shows rentalItem.name_en in tables/details, adds English-name inputs in add/edit modals with an auto-translate button wired to autoTranslate(), includes name_en in submit/edit requests, and clears nameEn after success.
Minor Cleanup / Styling
admin_view/nuxt-project/pages/rental_items/_id.vue, admin_view/nuxt-project/pages/rental_items/index.vue
Removed debug console.log, simplified delete call, minor template formatting, and added scoped CSS .h3-with-button.

Sequence Diagram

sequenceDiagram
    participant AdminUI as Admin UI
    participant Controller as RentalItemsController
    participant AppCtrl as ApplicationController
    participant Cache as Cache Store
    participant DeepL as DeepL API

    AdminUI->>Controller: POST/PUT /rental_items (name, [name_en])
    alt name_en absent
        Controller->>AppCtrl: translate_to_en(name)
        AppCtrl->>Cache: lookup(deepl_cache_key)
        alt Cache hit
            Cache-->>AppCtrl: cached translation
        else Cache miss
            AppCtrl->>DeepL: translate(name, target: EN-US)
            DeepL-->>AppCtrl: translated text
            AppCtrl->>Cache: store(key, translation, 7 days)
        end
        AppCtrl-->>Controller: translated name_en
        Controller->>Controller: set params[:name_en] = translated
    end
    Controller->>AdminUI: Success response (created/updated)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Suggested reviewers

  • YosukeIida

Poem

🐰 I hopped through fields of kanji and kana,
Brought DeepL wings to make names say "ta-da!"
Cached each phrase, kept errors at bay,
Now rentals speak English across the way —
A bunny's work, done in a fluffle and sway.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and clearly describes the main changes: adding English language support to the rental items list and implementing automatic translation via DeepL.
Linked Issues check ✅ Passed The PR implements all coding requirements: English name management in admin UI, DeepL translation integration, API modifications, and the necessary data binding for CRUD operations.
Out of Scope Changes check ✅ Passed All changes are within scope; modifications to rental items pages, API controller, routing, Gemfile, and compose.yml all support the issue objectives without unrelated additions.
Description check ✅ Passed The pull request description is comprehensive and well-structured, following the template with all key sections filled: issue reference, overview, detailed implementation, screenshots, test items, and notes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/harujo/add-en-rental-item-list

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
admin_view/nuxt-project/pages/rental_items/_id.vue (2)

140-164: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Normalize nameEn to an empty string before opening the edit modal.

If an item has a null name_en, the current assignment keeps the state null and the edit submit can send name_en=null. Defaulting to "" keeps the field controlled and preserves the backend's auto-translation path.

Proposed fix
-      this.nameEn = this.rentalItem.name_en;
+      this.nameEn = this.rentalItem.name_en || "";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@admin_view/nuxt-project/pages/rental_items/_id.vue` around lines 140 - 164,
In openEditModal, ensure nameEn is normalized to an empty string when
rentalItem.name_en is null so the edit form stays controlled; replace the direct
assignment this.nameEn = this.rentalItem.name_en with a null-coalescing
assignment (e.g. set to "" when rentalItem.name_en is null/undefined) so
submissions never send name_en=null and backend auto-translation is preserved.

195-207: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Serialize the PUT payload instead of concatenating the query string.

The edit request has the same encoding problem as the add flow, so English names with reserved characters will be mangled. Send a structured body/params object instead.

Proposed fix
-      const url =
-        "/rental_items/" +
-        this.routeId +
-        "?name=" +
-        this.name +
-        "&name_en=" +
-        this.nameEn +
-        "&is_inside_shop_rentable=" +
-        this.isInsideShopRentable +
-        "&is_outside_shop_rentable=" +
-        this.isOutsideShopRentable +
-        "&is_stage_rentable=" +
-        this.isStageRentable;
-
-      await this.$axios.$put(url).then((response) => {
+      const response = await this.$axios.$put(`/rental_items/${this.routeId}`, {
+        name: this.name,
+        name_en: this.nameEn,
+        is_inside_shop_rentable: this.isInsideShopRentable,
+        is_outside_shop_rentable: this.isOutsideShopRentable,
+        is_stage_rentable: this.isStageRentable,
+      });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@admin_view/nuxt-project/pages/rental_items/_id.vue` around lines 195 - 207,
The current update builds the PUT URL by concatenating query params (using
this.routeId, this.name, this.nameEn, this.isInsideShopRentable,
this.isOutsideShopRentable, this.isStageRentable) which causes encoding issues;
instead build a structured payload or params object and send it in the request
body (or as axios params) rather than embedding values into the path—replace the
concatenated url string with a call that uses the routeId only in the path and
passes { name, name_en: nameEn, is_inside_shop_rentable: isInsideShopRentable,
is_outside_shop_rentable: isOutsideShopRentable, is_stage_rentable:
isStageRentable } as the PUT body/params (ensuring proper key names used by the
API) and ensure the request library encodes the payload.
admin_view/nuxt-project/pages/rental_items/index.vue (1)

162-178: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Serialize the POST payload instead of concatenating the query string.

The current URL assembly will break once name_en contains reserved characters such as & or +. Send a structured body/params object so the new field is encoded correctly.

Proposed fix
-      const url =
-        "/rental_items/" +
-        "?name=" +
-        this.name +
-        "&name_en=" +
-        this.nameEn +
-        "&is_inside_shop_rentable=" +
-        this.isInsideShopRentable +
-        "&is_outside_shop_rentable=" +
-        this.isOutsideShopRentable +
-        "&is_stage_rentable=" +
-        this.isStageRentable;
-
-      this.$axios.$post(url).then((response) => {
+      const response = await this.$axios.$post("/rental_items", {
+        name: this.name,
+        name_en: this.nameEn,
+        is_inside_shop_rentable: this.isInsideShopRentable,
+        is_outside_shop_rentable: this.isOutsideShopRentable,
+        is_stage_rentable: this.isStageRentable,
+      });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@admin_view/nuxt-project/pages/rental_items/index.vue` around lines 162 - 178,
The code builds a query-string URL manually before calling this.$axios.$post
which will break on reserved characters; instead construct a payload object
(e.g., const payload = { name: this.name, name_en: this.nameEn,
is_inside_shop_rentable: this.isInsideShopRentable, is_outside_shop_rentable:
this.isOutsideShopRentable, is_stage_rentable: this.isStageRentable }) and call
this.$axios.$post("/rental_items", payload) (or pass as { params: payload } if
the API expects query params), then keep the existing response handling
(openSnackBar and clearing this.name/this.nameEn) — update the URL construction
block and the this.$axios.$post call in the same method to use the payload
object.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@api/app/controllers/rental_items_controller.rb`:
- Around line 15-24: Don't persist untranslated fallbacks into name_en: in both
create and update, call translate_to_en(params[:name]) into a local (e.g.,
translated) and only assign params[:name_en] when params[:name_en].blank? AND
translated.present? AND translated != params[:name] (i.e., the translation
succeeded and changed the text). Use the translated variable in place of direct
calls to translate_to_en in the create and update methods so you don't overwrite
an existing name_en with the source text on failure.

---

Outside diff comments:
In `@admin_view/nuxt-project/pages/rental_items/_id.vue`:
- Around line 140-164: In openEditModal, ensure nameEn is normalized to an empty
string when rentalItem.name_en is null so the edit form stays controlled;
replace the direct assignment this.nameEn = this.rentalItem.name_en with a
null-coalescing assignment (e.g. set to "" when rentalItem.name_en is
null/undefined) so submissions never send name_en=null and backend
auto-translation is preserved.
- Around line 195-207: The current update builds the PUT URL by concatenating
query params (using this.routeId, this.name, this.nameEn,
this.isInsideShopRentable, this.isOutsideShopRentable, this.isStageRentable)
which causes encoding issues; instead build a structured payload or params
object and send it in the request body (or as axios params) rather than
embedding values into the path—replace the concatenated url string with a call
that uses the routeId only in the path and passes { name, name_en: nameEn,
is_inside_shop_rentable: isInsideShopRentable, is_outside_shop_rentable:
isOutsideShopRentable, is_stage_rentable: isStageRentable } as the PUT
body/params (ensuring proper key names used by the API) and ensure the request
library encodes the payload.

In `@admin_view/nuxt-project/pages/rental_items/index.vue`:
- Around line 162-178: The code builds a query-string URL manually before
calling this.$axios.$post which will break on reserved characters; instead
construct a payload object (e.g., const payload = { name: this.name, name_en:
this.nameEn, is_inside_shop_rentable: this.isInsideShopRentable,
is_outside_shop_rentable: this.isOutsideShopRentable, is_stage_rentable:
this.isStageRentable }) and call this.$axios.$post("/rental_items", payload) (or
pass as { params: payload } if the API expects query params), then keep the
existing response handling (openSnackBar and clearing this.name/this.nameEn) —
update the URL construction block and the this.$axios.$post call in the same
method to use the payload object.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5fa39e5d-68fc-4fb1-8935-26b907fd5563

📥 Commits

Reviewing files that changed from the base of the PR and between 35ebba1 and 741abc2.

⛔ Files ignored due to path filters (1)
  • api/Gemfile.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • admin_view/nuxt-project/pages/rental_items/_id.vue
  • admin_view/nuxt-project/pages/rental_items/index.vue
  • api/Gemfile
  • api/app/controllers/application_controller.rb
  • api/app/controllers/rental_items_controller.rb

Comment on lines 15 to 24
def create
params[:name_en] = translate_to_en(params[:name]) if params[:name_en].blank?
@rental_item = RentalItem.create(rental_item_params)
render json: fmt(created, @rental_item)
end

def update
params[:name_en] = translate_to_en(params[:name]) if params[:name_en].blank?
@rental_item.update(rental_item_params)
render json: fmt(created, @rental_item, "Updated rental_item id = #{params[:id]}")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't persist an untranslated fallback into name_en.

If translate_to_en returns the source text when DeepL is unavailable or fails, both create and update will silently store Japanese in the English-name column. That makes the new field misleading in both the admin UI and the database.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@api/app/controllers/rental_items_controller.rb` around lines 15 - 24, Don't
persist untranslated fallbacks into name_en: in both create and update, call
translate_to_en(params[:name]) into a local (e.g., translated) and only assign
params[:name_en] when params[:name_en].blank? AND translated.present? AND
translated != params[:name] (i.e., the translation succeeded and changed the
text). Use the translated variable in place of direct calls to translate_to_en
in the create and update methods so you don't overwrite an existing name_en with
the source text on failure.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
admin_view/nuxt-project/pages/rental_items/index.vue (1)

175-198: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

URL-concatenated parameters in submit() are unencoded — special characters will break the request.

this.name and this.nameEn are appended directly into the query string. Names with spaces, &, =, +, or multibyte characters will silently corrupt the submitted values or truncate them.

Prefer sending parameters as a request body or using URLSearchParams:

🐛 Proposed fix (body-based POST)
-      const url =
-        "/rental_items/" +
-        "?name=" +
-        this.name +
-        "&name_en=" +
-        this.nameEn +
-        "&is_inside_shop_rentable=" +
-        this.isInsideShopRentable +
-        "&is_outside_shop_rentable=" +
-        this.isOutsideShopRentable +
-        "&is_stage_rentable=" +
-        this.isStageRentable;
-
-      this.$axios.$post(url).then((response) => {
+      this.$axios.$post("/rental_items/", {
+        name: this.name,
+        name_en: this.nameEn,
+        is_inside_shop_rentable: this.isInsideShopRentable,
+        is_outside_shop_rentable: this.isOutsideShopRentable,
+        is_stage_rentable: this.isStageRentable,
+      }).then((response) => {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@admin_view/nuxt-project/pages/rental_items/index.vue` around lines 175 - 198,
The submit() method builds a query string by concatenating this.name and
this.nameEn (and boolean flags) into url and calling this.$axios.$post(url),
which breaks for spaces/special/multibyte characters; change submit() to send a
proper POST body (e.g., pass an object with name, name_en,
is_inside_shop_rentable, is_outside_shop_rentable, is_stage_rentable to
this.$axios.$post) or at minimum build a safe query using encodeURIComponent /
URLSearchParams, update the call site where this.$axios.$post is invoked and
remove the manual string concatenation so response handling (openSnackBar, reset
fields, reload, closeAddModal) remains unchanged.
🧹 Nitpick comments (2)
api/config/routes.rb (1)

55-59: ⚡ Quick win

Regenerate OpenAPI documentation for the new /rental_items/translate endpoint.

The new collection route adds a public-facing POST endpoint.

As per coding guidelines: "Regenerate OpenAPI documentation with make openapi when updating API specifications."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/config/routes.rb` around lines 55 - 59, The routes change adds a new
public POST endpoint for rental_items (resources :rental_items with collection
post :translate), so regenerate the OpenAPI spec by running the project's
OpenAPI generation command (make openapi), verify the new
/rental_items/translate path appears in the generated docs, and commit the
updated generated OpenAPI files alongside this route change.
api/app/controllers/application_controller.rb (1)

56-66: 💤 Low value

translate_to_en will attempt translation on already-English text.

Unlike translate_to_ja, translate_to_en has no translatable_english_text? guard. If params[:name] already contains only Latin text (e.g. a user-supplied English name), it still burns a DeepL API call/quota. Since the translate_to_ja guard exists as a pattern, adding a symmetric guard here is low-effort.

♻️ Proposed fix
 def translate_to_en(text)
   return text if text.blank?
   return text if deepl_api_key.blank?
+  return text if translatable_english_text?(text)

   Rails.cache.fetch(deepl_cache_key('en', text), expires_in: 7.days) do
     request_deepl_translation(text, 'EN-US')
   end
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/app/controllers/application_controller.rb` around lines 56 - 66,
translate_to_en currently sends text to DeepL even for already-English
(Latin-only) input; add the same English-detection guard used by
translate_to_ja. Modify translate_to_en to return text if text.blank? or
deepl_api_key.blank? or unless translatable_english_text?(text) (i.e., skip
calling Rails.cache.fetch / request_deepl_translation for Latin-only strings),
keeping the existing caching (deepl_cache_key) and error rescue behavior;
reference functions: translate_to_en, translatable_english_text?,
deepl_cache_key, request_deepl_translation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@admin_view/nuxt-project/pages/rental_items/index.vue`:
- Around line 163-174: The autoTranslate method is building a query string with
raw this.name which breaks on Japanese characters; update the axios POST in
autoTranslate to send the text in the request body (e.g. post(url, { text:
this.name })) or, if you must use a query param, wrap the value with
encodeURIComponent(this.name); also fix the response access (axios $post returns
the body directly) by reading response.name_en instead of response.data.name_en;
apply the same change to the corresponding method in _id.vue.

In `@api/app/controllers/application_controller.rb`:
- Around line 68-78: The deepl env var is mismatched: request_deepl_translation
and deepl_api_key currently rely on ENV['DEEPL_API_KEY'] but the deepl-rb client
expects DEEPL_AUTH_KEY (or an explicit config), causing silent failures; fix by
either renaming your env var to DEEPL_AUTH_KEY or (preferred) add a Rails
initializer that calls DeepL.configure and sets config.auth_key =
ENV['DEEPL_API_KEY'] (and optionally config.host for free-tier) so the DeepL
client is properly authenticated before request_deepl_translation is called.

In `@api/app/controllers/rental_items_controller.rb`:
- Around line 32-35: The translate action is publicly accessible; protect it by
using the existing authentication filter. Add authenticate_api_user! to this
controller (or include :translate in an existing before_action that calls
authenticate_api_user!) so the translate method invokes authentication before
calling translate_to_en; update any controller-level before_action declarations
to include :translate (or add a new before_action :authenticate_api_user!, only:
[:translate]) to match the rest of the API.

---

Outside diff comments:
In `@admin_view/nuxt-project/pages/rental_items/index.vue`:
- Around line 175-198: The submit() method builds a query string by
concatenating this.name and this.nameEn (and boolean flags) into url and calling
this.$axios.$post(url), which breaks for spaces/special/multibyte characters;
change submit() to send a proper POST body (e.g., pass an object with name,
name_en, is_inside_shop_rentable, is_outside_shop_rentable, is_stage_rentable to
this.$axios.$post) or at minimum build a safe query using encodeURIComponent /
URLSearchParams, update the call site where this.$axios.$post is invoked and
remove the manual string concatenation so response handling (openSnackBar, reset
fields, reload, closeAddModal) remains unchanged.

---

Nitpick comments:
In `@api/app/controllers/application_controller.rb`:
- Around line 56-66: translate_to_en currently sends text to DeepL even for
already-English (Latin-only) input; add the same English-detection guard used by
translate_to_ja. Modify translate_to_en to return text if text.blank? or
deepl_api_key.blank? or unless translatable_english_text?(text) (i.e., skip
calling Rails.cache.fetch / request_deepl_translation for Latin-only strings),
keeping the existing caching (deepl_cache_key) and error rescue behavior;
reference functions: translate_to_en, translatable_english_text?,
deepl_cache_key, request_deepl_translation.

In `@api/config/routes.rb`:
- Around line 55-59: The routes change adds a new public POST endpoint for
rental_items (resources :rental_items with collection post :translate), so
regenerate the OpenAPI spec by running the project's OpenAPI generation command
(make openapi), verify the new /rental_items/translate path appears in the
generated docs, and commit the updated generated OpenAPI files alongside this
route change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7e8dcc00-70d7-4bde-b5d4-1756468262e4

📥 Commits

Reviewing files that changed from the base of the PR and between 741abc2 and 2b79f06.

📒 Files selected for processing (6)
  • admin_view/nuxt-project/pages/rental_items/_id.vue
  • admin_view/nuxt-project/pages/rental_items/index.vue
  • api/app/controllers/application_controller.rb
  • api/app/controllers/rental_items_controller.rb
  • api/config/routes.rb
  • compose.yml

Comment on lines +163 to +174
async autoTranslate() {
if (!this.name) return;
this.isTranslating = true;
try {
const response = await this.$axios.$post("/rental_items/translate?text=" + this.name);
this.nameEn = response.data.name_en;
} catch (e) {
this.openSnackBar("自動翻訳に失敗しました");
} finally {
this.isTranslating = false;
}
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Missing encodeURIComponent — Japanese names will corrupt the translate request.

this.name may contain Japanese characters (the entire feature is about translating Japanese names). Concatenating them raw into a query string produces malformed URLs in some environments and consistently wrong input to the backend.

The same issue is present in _id.vue Line 197.

🐛 Proposed fix
-        const response = await this.$axios.$post("/rental_items/translate?text=" + this.name);
+        const response = await this.$axios.$post("/rental_items/translate", { text: this.name });

Send the text as a JSON body instead (avoids encoding entirely). The backend params[:text] will still resolve correctly from a POST body in Rails.

Alternatively, if a query param is required:

-        const response = await this.$axios.$post("/rental_items/translate?text=" + this.name);
+        const response = await this.$axios.$post("/rental_items/translate?text=" + encodeURIComponent(this.name));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@admin_view/nuxt-project/pages/rental_items/index.vue` around lines 163 - 174,
The autoTranslate method is building a query string with raw this.name which
breaks on Japanese characters; update the axios POST in autoTranslate to send
the text in the request body (e.g. post(url, { text: this.name })) or, if you
must use a query param, wrap the value with encodeURIComponent(this.name); also
fix the response access (axios $post returns the body directly) by reading
response.name_en instead of response.data.name_en; apply the same change to the
corresponding method in _id.vue.

Comment on lines +68 to +78
def request_deepl_translation(text, target_lang)
translation = DeepL.translate(text, nil, target_lang)
translation&.text.presence || text
rescue DeepL::Exceptions::RequestError => e
Rails.logger.warn("[DeepL] request failed: #{e.class} #{e.message}")
text
end

def deepl_api_key
ENV['DEEPL_API_KEY'].presence
end
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check whether a DeepL initializer already exists elsewhere
fd -e rb -g '*deepl*'
# Also check for any DeepL.configure block in the codebase
rg -rn 'DeepL\.configure' --type rb

Repository: NUTFes/group-manager-2

Length of output: 93


🏁 Script executed:

#!/bin/bash
# Find any deepl-related files
fd -name '*deepl*'

# Search for DeepL configuration and env var usage (without file type filter)
rg 'DeepL\.' --max-count 20

# Search for both env var names
rg 'DEEPL_(API_KEY|AUTH_KEY)' --max-count 20

Repository: NUTFes/group-manager-2

Length of output: 499


🏁 Script executed:

#!/bin/bash
# Read the full application_controller.rb to understand context
wc -l api/app/controllers/application_controller.rb

Repository: NUTFes/group-manager-2

Length of output: 115


🏁 Script executed:

#!/bin/bash
# Check Gemfile for deepl-rb dependency
cat Gemfile | grep -i deepl

Repository: NUTFes/group-manager-2

Length of output: 106


🏁 Script executed:

#!/bin/bash
# Check for Gemfile in api directory
cat api/Gemfile | grep -i deepl

Repository: NUTFes/group-manager-2

Length of output: 99


🏁 Script executed:

#!/bin/bash
# Read the full application_controller.rb
cat -n api/app/controllers/application_controller.rb

Repository: NUTFes/group-manager-2

Length of output: 2979


🏁 Script executed:

#!/bin/bash
# Search for DEEPL_AUTH_KEY usage anywhere
rg 'DEEPL_AUTH_KEY'

Repository: NUTFes/group-manager-2

Length of output: 48


🏁 Script executed:

#!/bin/bash
# Search for any DeepL configuration blocks
rg -A 3 'DeepL\.configure'

Repository: NUTFes/group-manager-2

Length of output: 48


🌐 Web query:

deepl-rb gem environment variable DEEPL_AUTH_KEY default

💡 Result:

There is no default value for the deepl-rb environment variable DEEPL_AUTH_KEY. The gem docs instruct you to set DEEPL_AUTH_KEY to your DeepL API authentication key (example: export DEEPL_AUTH_KEY="your-api-token"). If you don’t provide an API key, the client cannot authenticate (i.e., the key is required rather than defaulted). [1][2][3] Related note: DeepL’s current API authentication best practice is to send the API key in the HTTP Authorization header (DeepL-Auth-Key ...), and DeepL has announced deprecation of legacy authentication methods such as sending an auth_key query parameter or request body field. [4]

Citations:


🌐 Web query:

deepl-rb github documentation authentication setup

💡 Result:

To authenticate the deepl-rb (DeepL Ruby library) you set your DeepL API authentication key (auth token) either via the environment variable DEEPL_AUTH_KEY or via the Ruby client configuration as auth_key. The underlying DeepL API uses an HTTP header Authorization: DeepL-Auth-Key [yourAuthKey] for authentication. 1) Get your DeepL authentication key - Create/get your DeepL API authentication key in your DeepL account. The key must be kept confidential [1]. 2) Use deepl-rb with environment variable (recommended) - Set DEEPL_AUTH_KEY to your API token: export DEEPL_AUTH_KEY="your-api-token" [2]. 3) Use deepl-rb with Ruby configuration - Configure the library’s auth_key: DeepL.configure do |config| config.auth_key = 'your-api-token' end [2]. 4) How authentication is sent over HTTP (what matters in production) - DeepL’s docs specify that the authentication key is provided via the Authorization HTTP header as: Authorization: DeepL-Auth-Key [yourAuthKey] [1]. - DeepL also notes a breaking change: after the specified date, authenticating by passing an auth_key in query parameters is disallowed, and requests must authenticate using an HTTP header named Authorization instead [3]. 5) (Optional) Configure host/version for deepl-rb - deepl-rb allows setting config.host (default https://api.deepl.com) and config.version (e.g., v1/v2) alongside config.auth_key [2]. Example from the library docs: DeepL.configure do |config| config.auth_key = 'your-api-token' config.host = 'https://api-free.deepl.com' config.version = 'v1' end [2].

Citations:


DEEPL_API_KEY env var name mismatch — translations will silently always fail.

The deepl-rb gem reads the authentication key from the environment variable DEEPL_AUTH_KEY, not DEEPL_API_KEY. The code uses ENV['DEEPL_API_KEY'] only as a guard to decide whether to attempt translation, but never configures the DeepL client with it. As a result:

  1. If only DEEPL_API_KEY is set, the guard passes but DeepL.translate will call the API without authentication, raising an auth error caught by rescue StandardError, and every call silently falls back to the original text.
  2. The translation feature will never work as written.

Fix: either rename the env variable to DEEPL_AUTH_KEY (which deepl-rb reads automatically), or add a Rails initializer that explicitly configures the client:

🔧 Proposed fix — add `config/initializers/deepl.rb`
# config/initializers/deepl.rb
DeepL.configure do |config|
  config.auth_key = ENV['DEEPL_API_KEY']
  # For the free-tier API endpoint:
  # config.host = 'https://api-free.deepl.com'
end
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/app/controllers/application_controller.rb` around lines 68 - 78, The
deepl env var is mismatched: request_deepl_translation and deepl_api_key
currently rely on ENV['DEEPL_API_KEY'] but the deepl-rb client expects
DEEPL_AUTH_KEY (or an explicit config), causing silent failures; fix by either
renaming your env var to DEEPL_AUTH_KEY or (preferred) add a Rails initializer
that calls DeepL.configure and sets config.auth_key = ENV['DEEPL_API_KEY'] (and
optionally config.host for free-tier) so the DeepL client is properly
authenticated before request_deepl_translation is called.

Comment on lines +32 to +35
def translate
translated = translate_to_en(params[:text])
render json: fmt(ok, { name_en: translated })
end
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how other controllers in the project handle authentication
rg -n 'before_action.*authenticate' --type rb -A2 -B2

Repository: NUTFes/group-manager-2

Length of output: 93


🏁 Script executed:

#!/bin/bash
# Find Ruby controller files and search for authentication patterns
fd -e rb -path '*/controllers/*' api/ | head -20

Repository: NUTFes/group-manager-2

Length of output: 235


🏁 Script executed:

#!/bin/bash
# Search for before_action with authenticate in Ruby files (without type flag)
rg 'before_action.*authenticate' -A2 -B2 api/

Repository: NUTFes/group-manager-2

Length of output: 5205


🏁 Script executed:

#!/bin/bash
# Read the target file to verify the current state of the translate action
cat -n api/app/controllers/rental_items_controller.rb

Repository: NUTFes/group-manager-2

Length of output: 1893


translate action has no authentication — anyone can drain your DeepL quota.

POST /rental_items/translate is publicly accessible with no before_action guard or rate limiting. A bad actor can call it repeatedly, exhausting DeepL API characters at your expense.

Add authentication protection consistent with the rest of the API using authenticate_api_user!:

🔒 Proposed fix
 class RentalItemsController < ApplicationController
+  before_action :authenticate_api_user!, except: %i[index show]
   before_action :set_rental_item, only: %i[show update destroy]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/app/controllers/rental_items_controller.rb` around lines 32 - 35, The
translate action is publicly accessible; protect it by using the existing
authentication filter. Add authenticate_api_user! to this controller (or include
:translate in an existing before_action that calls authenticate_api_user!) so
the translate method invokes authentication before calling translate_to_en;
update any controller-level before_action declarations to include :translate (or
add a new before_action :authenticate_api_user!, only: [:translate]) to match
the rest of the API.

@KokiWakatsuki
Copy link
Copy Markdown
Collaborator

備考に,DEEPL_API_KEYしか記載されてないけど,DEEPL_AUTH_KEYも設定してない?
DEEPL_API_KEYだけだと翻訳できない.
deepl-rbはデフォルトだと,DEEPL_AUTH_KEYを読み込むはず.

@KokiWakatsuki KokiWakatsuki self-requested a review May 7, 2026 07:53
@KokiWakatsuki KokiWakatsuki self-assigned this May 7, 2026
@KokiWakatsuki
Copy link
Copy Markdown
Collaborator

物品名に,スペースとか&とかが入るとエンコードできなくて,翻訳されないです.
マニュアルとかで制限されてるなら,無視して大丈夫です!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feat] adminの物品一覧で英語名を管理できるようにする

2 participants