[feat] 物品一覧ページの英語対応、英語名の編集機能を作成しました。また、DeepLによる自動翻訳機能も追加しました#2037
[feat] 物品一覧ページの英語対応、英語名の編集機能を作成しました。また、DeepLによる自動翻訳機能も追加しました#2037haruto-kamijo wants to merge 5 commits intogm3/developfrom
Conversation
📝 WalkthroughWalkthroughAdds admin-side English name support for rental items: UI shows/edits ChangesRental items English-name feature
Sequence DiagramsequenceDiagram
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)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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.
Built for teams:
One agent for your entire SDLC. Right inside Slack. 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. Comment |
There was a problem hiding this comment.
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 winNormalize
nameEnto an empty string before opening the edit modal.If an item has a
nullname_en, the current assignment keeps the state null and the edit submit can sendname_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 winSerialize 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 winSerialize the POST payload instead of concatenating the query string.
The current URL assembly will break once
name_encontains 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
⛔ Files ignored due to path filters (1)
api/Gemfile.lockis excluded by!**/*.lock
📒 Files selected for processing (5)
admin_view/nuxt-project/pages/rental_items/_id.vueadmin_view/nuxt-project/pages/rental_items/index.vueapi/Gemfileapi/app/controllers/application_controller.rbapi/app/controllers/rental_items_controller.rb
| 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]}") |
There was a problem hiding this comment.
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.
…es/group-manager-2 into feat/harujo/add-en-rental-item-list
There was a problem hiding this comment.
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 winURL-concatenated parameters in
submit()are unencoded — special characters will break the request.
this.nameandthis.nameEnare 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 winRegenerate OpenAPI documentation for the new
/rental_items/translateendpoint.The new collection route adds a public-facing POST endpoint.
As per coding guidelines: "Regenerate OpenAPI documentation with
make openapiwhen 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_enwill attempt translation on already-English text.Unlike
translate_to_ja,translate_to_enhas notranslatable_english_text?guard. Ifparams[:name]already contains only Latin text (e.g. a user-supplied English name), it still burns a DeepL API call/quota. Since thetranslate_to_jaguard 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
📒 Files selected for processing (6)
admin_view/nuxt-project/pages/rental_items/_id.vueadmin_view/nuxt-project/pages/rental_items/index.vueapi/app/controllers/application_controller.rbapi/app/controllers/rental_items_controller.rbapi/config/routes.rbcompose.yml
| 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; | ||
| } | ||
| }, |
There was a problem hiding this comment.
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.
| 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 |
There was a problem hiding this comment.
🧩 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 rbRepository: 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 20Repository: 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.rbRepository: NUTFes/group-manager-2
Length of output: 115
🏁 Script executed:
#!/bin/bash
# Check Gemfile for deepl-rb dependency
cat Gemfile | grep -i deeplRepository: NUTFes/group-manager-2
Length of output: 106
🏁 Script executed:
#!/bin/bash
# Check for Gemfile in api directory
cat api/Gemfile | grep -i deeplRepository: 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.rbRepository: 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:
- 1: https://www.github.com/deeplcom/deepl-rb
- 2: https://www.rubydoc.info/gems/deepl-rb/3.0.1
- 3: https://gem.sh/gems/deepl-rb/v2.1.0/readme
- 4: https://github.com/wikiti/deepl-rb
🌐 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:
- 1: https://developers.deepl.com/docs/getting-started/auth
- 2: https://www.github.com/deeplcom/deepl-rb
- 3: https://developers.deepl.com/docs/resources/breaking-changes-change-notices/march-2025-deprecating-get-requests-to-translate-and-authenticating-with-auth_key
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:
- If only
DEEPL_API_KEYis set, the guard passes butDeepL.translatewill call the API without authentication, raising an auth error caught byrescue StandardError, and every call silently falls back to the original text. - 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.
| def translate | ||
| translated = translate_to_en(params[:text]) | ||
| render json: fmt(ok, { name_en: translated }) | ||
| end |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check how other controllers in the project handle authentication
rg -n 'before_action.*authenticate' --type rb -A2 -B2Repository: 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 -20Repository: 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.rbRepository: 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.
|
備考に,DEEPL_API_KEYしか記載されてないけど,DEEPL_AUTH_KEYも設定してない? |
|
物品名に,スペースとか&とかが入るとエンコードできなくて,翻訳されないです. |
対応Issue
resolve #2024
概要
name_en)を追加し、一覧・詳細・追加・編集画面で管理できるようにした実装詳細
admin_view/nuxt-project/pages/rental_items/index.vueadmin_view/nuxt-project/pages/rental_items/_id.vueapi/app/controllers/rental_items_controller.rbcreate/update時にname_enが空の場合、DeepL で自動翻訳して保存POST /rental_items/translateエンドポイントを追加(フロントからの翻訳リクエスト用)api/app/controllers/application_controller.rbtranslate_to_enメソッドを追加(translate_to_jaと対称な実装)api/config/routes.rbPOST /rental_items/translateのルートを追加compose.ymlapi/.envを読み込むenv_file設定を追加(DeepL APIキーの受け渡し)画面スクリーンショット等
テスト項目
備考
api/.envにDEEPL_API_KEYとして設定が必要(Free プランの場合キー末尾が:fx)compose.ymlにapi/.envのenv_file指定を追加しているため、既存のwebhook.envとの併用となっている