원클릭으로
embroiderly-i18n
Use this skill when adding, editing, or reorganizing translations in the main app.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Use this skill when adding, editing, or reorganizing translations in the main app.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Use when the user wants to design or improve a module's interface, find deepening opportunities, decide where a seam goes, make code more testable or AI-navigable, or when another skill needs the deep-module vocabulary.
Use it to load and enforce domain glossary throughout the task.
Use when writing, editing, or restructuring a guide, reference page, or any content under the `docs/` directory.
Make changes to the UI Kit. Use this skill when you need to create new or update existing UI components, their themes, stories, or tests.
Verify code quality after making changes in this project. Use this skill whenever you've finished implementing a feature, fix, or refactor and need to run the verification pipeline.
Use when the user wants to stress-test a plan before building, or uses any 'grill' trigger phrases.
| name | embroiderly-i18n |
| description | Use this skill when adding, editing, or reorganizing translations in the main app. |
Embroiderly uses Fluent for localization.
The only file you may edit is app/src/assets/locales/en.ftl.
Other locale files (e.g. uk.ftl) are maintained by human translators.
Before adding a new key, read the whole en.ftl to check for an existing one that covers the same meaning.
key = Hello, world!
Terms start with - and can only be referenced, but never retrieved directly.
Use for brand names and shared vocabulary.
-app-name = Embroiderly
quit = Quit { -app-name } # good — references the term
quit = Quit Embroiderly # wrong — hardcodes the name
Runtime values from the app are referenced with $.
greeting = Welcome, { $user }!
file-removed = Removed "{ $name }".
Reference another message to keep UI strings consistent.
Note the missing $ sign.
menu-save = Save
help-save = Click { menu-save } to save the file.
Group related strings for one UI element under .attribute-name.
Fetch them via $ta in code.
A message may have only attributes and no primary value (standard for dialogs, notifications, settings rows).
# Good — attributes group the same dialog together
dialog-confirm =
.title = Remove Layer
.description = Are you sure? This action can be undone.
# Wrong — separate top-level keys for the same element
dialog-confirm-title = Remove Layer
dialog-confirm-description = Are you sure? This action can be undone.
* marks the required default variant.
Numeric selectors match CLDR plural categories (zero, one, two, few, many, other).
palette-size = Palette: { $size ->
[0] empty
[one] { $size } color
*[other] { $size } colors
}
Indent every continuation line by at least one space. Common indentation is stripped automatically.
update-description =
A new version is available.
Current: { $current }. Latest: { $latest }.
### File-level comment.
## Section group header.
# Per-message comment explaining variables or context.
key = Value with { $variable }
Fluent automatically formats numbers and dates according to the active locale.
The built-in functions let you override that formatting — they map directly to the JS Intl API.
NUMBER($var, …options) -> Intl.NumberFormat options:
emails = You have { $count } unread emails. # auto-formatted
price = Price: { NUMBER($value, maximumFractionDigits: 2) } # explicit decimal places
count = { NUMBER($total, useGrouping: false) } stitches # no thousands separator
DATETIME($var, …options) -> Intl.DateTimeFormat options:
last-checked = Last checked: { DATETIME($date, dateStyle: "long") }.
release-date = Released { DATETIME($date, month: "long", day: "numeric", year: "numeric") }.
Use these functions when the default locale formatting is not specific enough, or when the formatted value is also used as a selector (the same options must be applied to both):
emails = { NUMBER($count, useGrouping: false) ->
[0] No unread emails.
[one] One unread email.
*[other] { NUMBER($count, useGrouping: false) } unread emails.
}
## section header.
Extend existing groups rather than coining a new prefix.modal-*, confirm-*, history-*, error, choose-file).<!-- message value -->
{{ $t("key") }}
:label="$t('key')"
<!-- with variables -->
:label="$t('canvas-layers-remove', { name: layer.name })"
<!-- rich text with embedded Vue components -->
<i18n tag="p" path="welcome-get-started">
<template #button-open="{ buttonOpenLabel }">
<Button :label="buttonOpenLabel" @click="open" />
</template>
</i18n>
Attribute slots in <i18n> are kebab-cased attribute names from the .ftl message:
welcome-get-started =
{ $button-open } to get started.
.button-open-label = Open a pattern
useI18n)import { useI18n } from "~/composables/";
const { fluent, currentLocale, setLocale } = useI18n();
// Message value
fluent.$t("key");
fluent.$t("key", { count: 5 });
// All attributes as a plain object — spread directly into dialog/toast/panel props
fluent.$ta("dialog-confirm"); // { title: "Remove Layer", description: "Are you sure? ..." }
const accepted = await confirm.open(fluent.$ta("canvas-layers-remove-confirm", { name })).result;
toast.add({ type: "foreground", ...fluent.$ta("updater-unsaved-changes") });
$ta is the standard way to pass { title, description } to dialogs, toasts, and confirm modals.