بنقرة واحدة
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.