Internationalization and Localization
Use this skill when software needs to present text, formatting, or UI behavior for more than one locale. Internationalization (i18n) makes the product localizable; localization (l10n) provides locale-specific messages, formats, and conventions.
Project Fluent is a localization system and message syntax designed to let
localizers express natural-language complexity such as plurals, gender, case,
conjugation, and word order in translation files instead of application code.
Fluent resources are usually written as Fluent Translation List (.ftl) files.
Use When
- Adding or reviewing user-facing strings, message catalogs, locale folders, translation IDs, fallback chains, or locale negotiation.
- Writing or editing
.ftlfiles, Fluent selectors, variables, terms, attributes, translator comments, or localized number/date formatting. - Integrating Project Fluent through
fluent.js,python-fluent,fluent-rs, or framework wrappers built on those packages. - Testing locale-specific output, fallback behavior, missing translations, pseudolocalization, right-to-left/bidirectional layout, or accessibility text.
- Implementing or reviewing localized PHP messages, locale negotiation, formatters, Gettext, framework catalogs, or PHP/Smarty rendering boundaries.
Do not use this skill for copyediting a single fixed-language message with no
localization surface. Use language engineering skills for implementation
mechanics, css-scss-styling for layout and
writing-direction styling, api-design for public
locale contract design, playwright-e2e for
browser-visible localization tests, and
documentation-engineering for docs-only
localization guidance. For Piwigo, piwigo-plugin-engineering
owns catalog paths, host translation helpers, Smarty integration, and plugin
compatibility; this skill owns message design, formatting, fallback, bidi,
pseudolocalization, and localization testing.
Workflow
- Inspect local conventions first: existing locale directories, source locale, message ID style, extraction/build scripts, package versions, fallback order, tests, and translation review workflow.
- Treat American English (
en-US) as the default/source locale unless the repository explicitly defines another source locale. Keep source messages clear and complete; do not use sentence fragments that force other languages to copy English grammar. - Decide whether the project needs full i18n or a smaller scoped change. Use Project Fluent when localizers need control over pluralization, grammatical variants, markup overlays, attributes, or cross-language word order; a simpler key/value library may be enough for a prototype or fixed internal tool.
- Put language decisions in the catalog, not in application string assembly. Pass structured variables such as counts, dates, names, and states into Fluent; let the locale choose wording and order.
- Design fallback before implementation. Typical order is requested locale,
language fallback when supported, then
en-US. Test missing-message and missing-variable behavior instead of assuming silent success. - Apply the negotiated BCP 47 language and script-aware direction with
langanddiron the document or localized subtree. Use logical CSS properties; do not infer direction from arbitrary user strings. - Consult current official upstream documentation before changing version-sensitive binding APIs, framework adapters, CLI tools, or parser/validator behavior.
- Verify with the repository's parser, linter, typecheck, unit tests, snapshot tests, E2E tests, or build-time catalog validation.
Fluent .ftl Authoring Rules
Use stable, semantic message IDs:
checkout-submit,profile-greeting,error-network-timeout. Avoid IDs that encode English text or UI position.Prefer complete sentences or complete UI labels. Do not concatenate localized fragments in code.
Use variables/placeables for runtime data:
welcome = Welcome, { $user }!. Keep variable names meaningful and pass typed values where the runtime supports locale-aware number/date formatting.Use selectors for plurals, gender, grammatical case, or state. Always include a default variant marked with
*.inbox-count = { $count -> [one] One message *[other] { $count } messages }Use CLDR plural categories such as
one,few,many, andotherinstead of hard-coding English assumptions. Some languages do not use English-style plural forms; Japanese commonly uses one form where English uses singular and plural; Spanish needs gender and agreement in many messages.Use terms for reusable product vocabulary and brand names:
-brand-name = ExampleApp about-title = About { -brand-name }Use parameterized terms only when localizers need grammatical variants, such as case or gender. Do not expose terms directly as user-facing messages.
Use attributes to group text for one UI element, including accessible labels:
email-input = Email .hint = name@example.com .aria-label = Email addressAdd translator comments for placeholders, ambiguous terms, character limits, tone, variables, and UI context. Use group or file comments for broader context.
Keep
.ftlindentation space-based. Preserve Unicode text. Escape literal braces or leading special characters according to Fluent syntax rules.
Locale and Language Guidance
- Use BCP 47 locale tags such as
en-US,es,es-MX,ja, orja-JP. Store locale-specific files in predictable paths, for examplelocales/en-US/app.ftl,locales/es/app.ftl, andlocales/ja/app.ftl, unless the repository already has a convention. - Keep
en-UScomplete. Other locales may be partial only if fallback behavior is intentional, visible in tests, and acceptable for the product. - For Spanish, avoid baking gender, number, or formality choices into code. Let
translators choose variants and agreement in
.ftl. - For Japanese, avoid assumptions about spaces, capitalization, or English plural categories. Let the message own sentence order and politeness level.
- Test long strings, short strings, missing translations, bidirectional content, pseudolocalized text, and accessible names. Test a real RTL locale and mixed-direction content, not only a flipped layout. Include screen-reader text and ARIA attributes in localization review when they are user-visible.
Binding Guidance
Use local dependency versions and current upstream docs before changing APIs.
- JavaScript/TypeScript:
fluent.jspackages include@fluent/bundlefor coreFluentBundle/FluentResourceformatting,@fluent/syntaxfor parsing and tooling,@fluent/langnegfor locale negotiation,@fluent/domfor DOM localization, and@fluent/reactfor React integration. Usejavascript-typescript-engineeringfor package-manager, build, and type/test mechanics. - Python:
python-fluentprovidesfluent.syntaxfor parsing, serialization, AST tooling, and analysis;fluent.runtimeforFluentLocalization,FluentResourceLoader,format_value, andformat_message; andfluent.pygmentsfor syntax highlighting. Usepython-engineeringfor packaging, typing, tests, and Python implementation structure. - PHP: discover the installed
ext-intlversion and project conventions before choosingMessageFormatter,NumberFormatter,IntlDateFormatter, locale negotiation, or existing Gettext/framework catalogs. Do not impose Fluent on PHP projects; loadphp-engineeringfor PHP implementation and template mechanics. - Rust:
fluent-rsincludes crates such asfluent,fluent-bundle,fluent-fallback,fluent-resmgr,fluent-syntax,fluent-pseudo,fluent-testing, andfluent-cli. Low-level bundles format messages for a locale or locale chain; higher-level fallback/resource-manager crates can own resource loading and fallback. Userust-engineeringandrust-testing-qualityfor Rust code and quality gates.
Testing and Validation
- Keep catalog and runtime validation separate. In the catalog lane, parse and add each resource with the repository's pinned binding or parser, checking parse and add-resource errors such as syntax failures, duplicate IDs, or missing default variants.
- In the runtime call-site lane, format through the pinned binding with representative arguments; cover selector branches, fallback, locale-specific number/date output, missing translations, unknown message, term, attribute, or function references, and missing-argument/error handling. Format every relevant message and attribute, or use a binding-specific semantic analyzer that proves equivalent reference coverage. Test message IDs and accessible labels at the call sites that render them.
- Add regression tests near the code that formats messages; add E2E coverage when locale affects layout, navigation, ARIA names, forms, direction, or user-visible flows.
- Use pseudolocalization or long-string fixtures to catch clipped text, concatenation assumptions, and layout coupling.
Security and Accessibility
- Load
security-reviewandsecurity-review-evidencewhen localized content, locale tags, translation files, templates, Markdown/HTML, user-generated text, remote catalogs, or interpolation cross a trust boundary. - Do not assume Fluent sanitizes output for every sink. Escape or sanitize for
the target context, and do not insert translated strings into
innerHTMLunless an official, reviewed overlay mechanism is used safely. - Treat translator-controlled markup and attributes as security-sensitive. In DOM/React integrations, prefer official Fluent overlay/component APIs and allow translated attributes only when the sink is safe and intentional.
- Validate and normalize locale inputs before using them in file paths, URLs, cache keys, or database queries.
- Localize visible labels, placeholders, titles, errors, and ARIA attributes together so screen-reader and keyboard users receive the same meaning as visual users.
References
- Project Fluent syntax guide: https://projectfluent.org/fluent/guide/
- Fluent specification: https://github.com/projectfluent/fluent
fluent.js: https://github.com/projectfluent/fluent.jspython-fluent: https://projectfluent.org/python-fluent/fluent-rs: https://github.com/projectfluent/fluent-rs
Zod Error Localization
When Zod is selected, load zod-engineering. Translate stable issue codes plus bounded fields at render time; test precedence/fallback, avoid per-request global z.config() mutation, and never expose raw input.