### Install Gettext Sigils with Igniter Source: https://github.com/zebbra/gettext_sigils/blob/main/README.md Use the igniter mix task to install and set up the gettext_sigils library. ```bash mix igniter.install gettext_sigils ``` -------------------------------- ### Example Prompt for Todo App Creation Source: https://github.com/zebbra/gettext_sigils/blob/main/guides/llm.md Use this prompt to instruct an LLM agent to create a simple todo application. The agent should utilize `~t` sigils for translatable strings. ```plaintext user: create a simple todo app ``` -------------------------------- ### Example Prompt for Translating User List Page Source: https://github.com/zebbra/gettext_sigils/blob/main/guides/llm.md Use this prompt to instruct an LLM agent to translate the user list page. The agent should detect available modifiers and use `~t` sigils. ```plaintext user: translate the user list page ``` -------------------------------- ### Configure Domain and Context per Module Source: https://context7.com/zebbra/gettext_sigils/llms.txt Configure default Gettext domain and context per module using the `:sigils` option. This example shows how to set domain and context for admin and public pages. ```elixir defmodule MyAppWeb.AdminDashboard do use GettextSigils, backend: MyApp.Gettext, sigils: [ domain: "admin", context: "dashboard" ] def page_title do # Uses domain: "admin", context: "dashboard" ~t"Admin Dashboard" # => dpgettext(MyApp.Gettext, "admin", "dashboard", "Admin Dashboard") end def user_count(count) do ~t"#{count} active users" # => dpgettext(MyApp.Gettext, "admin", "dashboard", "%{count} active users", count: count) end end defmodule MyAppWeb.PublicPages do use GettextSigils, backend: MyApp.Gettext, sigils: [ domain: "frontend" # context defaults to nil ] def welcome do ~t"Welcome to our site" # => dgettext(MyApp.Gettext, "frontend", "Welcome to our site") end end ``` -------------------------------- ### Gettext Sigils Interpolation Examples Source: https://context7.com/zebbra/gettext_sigils/llms.txt Demonstrates how the `~t` sigil automatically handles various interpolation scenarios, including variables, Phoenix assigns, dot access, function calls, and explicit key assignments. ```elixir defmodule MyApp.Messages do use GettextSigils, backend: MyApp.Gettext def examples do name = "Alice" color = "red" fruit = %{name: "apple"} count = 5 # Variable -> key is the variable name ~t"Hello, #{name}" # => gettext("Hello, %{name}", name: name) # Phoenix assigns -> key strips the @ ~t"Welcome, #{@user_name}" # => gettext("Welcome, %{user_name}", user_name: @user_name) # Dot access -> keys joined with underscore ~t"The #{fruit.name} is #{color}" # => gettext("The %{fruit_name} is %{color}", fruit_name: fruit.name, color: color) # Remote function -> module_function format ~t"Result: #{String.upcase(name)}" # => gettext("Result: %{string_upcase}", string_upcase: String.upcase(name)) # Local function -> function name as key ~t"Status: #{format_status(order)}" # => gettext("Status: %{format_status}", format_status: format_status(order)) # Duplicate keys with same expression are deduplicated ~t"#{name} is #{name}" # => gettext("%{name} is %{name}", name: name) # Explicit key assignment for complex expressions or disambiguation ~t"Sum: #{total = calculate_total(items)}" # => gettext("Sum: %{total}", total: calculate_total(items)) # Resolving ambiguous keys with explicit assignment ~t"#{x = Foo.bar()} differs from #{y = foo.bar}" # => gettext("%{x} differs from %{y}", x: Foo.bar(), y: foo.bar) end end ``` -------------------------------- ### Configure usage_rules for GettextSigils Source: https://github.com/zebbra/gettext_sigils/blob/main/guides/llm.md Add GettextSigils to your `usage_rules` configuration in `mix.exs` to enable LLM agents to generate translatable code. This setup ensures agents use `~t` sigils instead of fixed strings. ```elixir defp usage_rules do [ file: "AGENTS.md", usage_rules: [ :gettext_sigils ] ] end ``` -------------------------------- ### Replace Gettext with GettextSigils Source: https://context7.com/zebbra/gettext_sigils/llms.txt Replace `use Gettext` with `use GettextSigils` to enable the `~t` sigil in your module. This example shows basic translation usage with and without interpolation. ```elixir defmodule MyAppWeb.Components do # Replace this: # use Gettext, backend: MyApp.Gettext # With this: use GettextSigils, backend: MyApp.Gettext def greeting(user) do # Before: gettext("Hello, %{name}!", name: user.name) # After: ~t"Hello, #{user.name}!" end def welcome do # Simple translation without interpolation ~t"Welcome to our application!" end def status(order) do # Multiple interpolations are automatically handled ~t"Order #{order.id} for #{order.customer_name} is #{order.status}" # Compiles to: gettext("Order %{order_id} for %{order_customer_name} is %{order_status}", # order_id: order.id, order_customer_name: order.customer_name, order_status: order.status) end end ``` -------------------------------- ### Basic Sigil Modifiers Source: https://github.com/zebbra/gettext_sigils/blob/main/guides/modifiers.md Examples of applying single modifiers to the ~t sigil. ```elixir ~t"Not found"e # override the Gettext domain ~t"#{count} error(s)"N # pluralize ~t"hello"u # transform the result (e.g. upcase) ``` -------------------------------- ### Search Documentation with mix usage_rules.docs Source: https://github.com/zebbra/gettext_sigils/blob/main/AGENTS.md Use these commands to find documentation for modules and functions within the current project or its dependencies. ```elixir # Search a whole module mix usage_rules.docs Enum # Search a specific function mix usage_rules.docs Enum.zip # Search a specific function & arity mix usage_rules.docs Enum.zip/1 ``` -------------------------------- ### Search Documentation with mix usage_rules.search_docs Source: https://github.com/zebbra/gettext_sigils/blob/main/AGENTS.md Use these commands to perform broader searches across packages and documentation titles. ```elixir # Search docs for all packages in the current application, including Elixir mix usage_rules.search_docs Enum.zip # Search docs for specific packages mix usage_rules.search_docs Req.get -p req # Search docs for multi-word queries mix usage_rules.search_docs "making requests" -p req # Search only in titles (useful for finding specific functions/modules) mix usage_rules.search_docs "Enum.zip" --query-by title ``` -------------------------------- ### Configure Gettext Sigils Options Source: https://context7.com/zebbra/gettext_sigils/llms.txt Demonstrates full configuration options for GettextSigils, including domain, context, and various modifier definitions. ```elixir defmodule MyApp.FullExample do use GettextSigils, # Required: Gettext backend module backend: MyApp.Gettext, # Optional: sigils configuration sigils: [ # Default domain for all ~t sigils in this module # Use :default to follow backend's configured default domain # Use a binary string to override domain: "frontend", # Default context for all ~t sigils in this module # Use nil for no context (default) # Use a binary string to set context context: inspect(__MODULE__), # Modifier definitions (lowercase letters a-z only) # Uppercase letters are reserved for built-ins (N for pluralization) modifiers: [ # Keyword list form: override domain and/or context e: [domain: "errors"], g: [domain: :default, context: nil], # Module form: custom modifier without options u: MyApp.UpcaseModifier, # Module with options form: custom modifier with configuration s: {MyApp.ShoutModifier, intensity: 3} ] ] # All standard Gettext macros remain available def mixed_usage do # Use the sigil msg1 = ~t"Hello, #{@name}" # Or use standard Gettext macros msg2 = gettext("Hello, World!") msg3 = dgettext("errors", "Not found") msg4 = pgettext("menu", "File") {msg1, msg2, msg3, msg4} end end ``` -------------------------------- ### Configure ExDoc Admonitions Source: https://github.com/zebbra/gettext_sigils/blob/main/AGENTS.md Use these blocks in documentation to highlight important information with specific classes. ```markdown > #### Reserved letters {: .info} > > Custom modifiers must use lowercase letters (`a`–`z`). ``` -------------------------------- ### Configure usage_rules with GettextSigils Skills Source: https://github.com/zebbra/gettext_sigils/blob/main/guides/llm.md Add GettextSigils to your `usage_rules` configuration in `mix.exs` to include optional skills for LLM agents. This allows agents to discover and use localization functionalities. ```elixir defp usage_rules do [ file: "AGENTS.md", usage_rules: [ :gettext_sigils ], skills: [ package_skills: [:gettext_sigils] ] ] end ``` -------------------------------- ### Mix Task Help in Elixir Source: https://github.com/zebbra/gettext_sigils/blob/main/AGENTS.md Use `mix help` to list all available mix tasks. For detailed documentation on a specific task, use `mix help task_name`. ```elixir mix help ``` ```elixir mix help task_name ``` -------------------------------- ### Basic ~t Sigil Usage Source: https://github.com/zebbra/gettext_sigils/blob/main/README.md Demonstrates the basic usage of the ~t sigil for simple string translations. ```elixir ~t"Hello, World!" # same as gettext("Hello, World!") ``` -------------------------------- ### Setting Default Domain and Context Source: https://github.com/zebbra/gettext_sigils/blob/main/README.md Configures default Gettext domain and context per module using the :sigils option. ```elixir use GettextSigils, backend: MyApp.Gettext, sigils: [ domain: "frontend", context: "dashboard" ] ~t"Welcome" # => dpgettext(MyApp.Gettext, "frontend", "dashboard", "Welcome") ``` -------------------------------- ### Generate and Sync Translations Source: https://github.com/zebbra/gettext_sigils/blob/main/usage-rules/skills/gettext-sigils-localization/SKILL.md Use the mix task to extract new strings and synchronize them with existing .po files. Run the check command to verify the status of translations. ```bash mix gettext.extract --merge --sync ``` ```bash mix gettext.extract --check-up-to-date ``` -------------------------------- ### Configure Domain, Context, and Modifiers Source: https://github.com/zebbra/gettext_sigils/blob/main/usage-rules.md Define default domain and context, and configure modifiers for overriding them per `~t` sigil call. Modifiers are defined within the `sigils` option. ```elixir use GettextSigils, backend: MyApp.Gettext, # domain: "default", # context: nil sigils: [ modifiers: [ e: [domain: "errors"], m: [context: inspect(__MODULE__)] ] ] ~t"Welcome" # domain: "default", context: nil ~t"Not found"e # domain: "errors", context: nil ~t"Hello"m # domain: "default", context: "MyApp.SomeModule" ~t"Oops"em # domain: "errors", context: "MyApp.SomeModule" ``` -------------------------------- ### Synchronous Requests with GenServer.call/3 Source: https://github.com/zebbra/gettext_sigils/blob/main/AGENTS.md Use `GenServer.call/3` for synchronous requests that expect a reply. Ensure appropriate timeouts are set. ```elixir GenServer.call(server, request, timeout) ``` -------------------------------- ### Using Keyword Lists for Options in Elixir Source: https://github.com/zebbra/gettext_sigils/blob/main/AGENTS.md Prefer keyword lists for passing options to functions. This improves readability and maintainability for configuration parameters. ```elixir [timeout: 5000, retries: 3] ``` -------------------------------- ### Use explicit keys Source: https://github.com/zebbra/gettext_sigils/blob/main/guides/interpolation.md Assign explicit keys using the key = expr syntax to resolve collisions or improve readability. ```elixir ~t"Order status: #{status = order_status(resp[field])}" # => gettext("Order status: %{status}", status: order_status(resp[field])) ~t"Valid: #{x = Foo.bar()} != #{y = foo.bar}" # => gettext("Valid: %{x} != %{y}", x: Foo.bar(), y: foo.bar) ``` -------------------------------- ### Prepending to Lists in Elixir Source: https://github.com/zebbra/gettext_sigils/blob/main/AGENTS.md Prefer prepending elements to lists using the `[new | list]` syntax for better performance compared to appending with `++`. ```elixir [new | list] ``` -------------------------------- ### Replace Gettext with GettextSigils Source: https://github.com/zebbra/gettext_sigils/blob/main/usage-rules.md Switch from `use Gettext` to `use GettextSigils` in modules requiring translations. Ensure the `backend` option is correctly set. ```elixir use Gettext, backend: MyApp.Gettext # with this use GettextSigils, backend: MyApp.Gettext ``` -------------------------------- ### Configure and Use Custom Modifiers Source: https://github.com/zebbra/gettext_sigils/blob/main/guides/modifiers.md Wire up custom modifiers using the `:modifiers` option in `use GettextSigils`. Modifiers can be specified as bare module atoms or as `{module, opts}` tuples. The order of application matters. ```elixir use GettextSigils, backend: MyApp.Gettext, sigils: [ domain: "frontend", modifiers: [ u: MyApp.UpcaseModifier, s: {MyApp.ShoutModifier, intensity: 3} ] ] ~t"hello"u # => "HELLO" ~t"hello"s # => "hello!!!" ~t"hello"us # => "HELLO!!!" (upcase runs first, then shout) ``` -------------------------------- ### Use GettextSigils Instead of Gettext Source: https://github.com/zebbra/gettext_sigils/blob/main/README.md Replace 'use Gettext' with 'use GettextSigils' in your module to enable the ~t sigil. ```elixir # replace this use Gettext, backend: MyApp.Gettext # with this use GettextSigils, backend: MyApp.Gettext ``` -------------------------------- ### Fire-and-Forget Messages with GenServer.cast/2 Source: https://github.com/zebbra/gettext_sigils/blob/main/AGENTS.md Use `GenServer.cast/2` for asynchronous, fire-and-forget messages. Prefer `call` over `cast` when back-pressure is a concern. ```elixir GenServer.cast(server, request) ``` -------------------------------- ### Running Specific Tests in Elixir Source: https://github.com/zebbra/gettext_sigils/blob/main/AGENTS.md Execute tests in a specific file using `mix test test/my_test.exs`. To run a particular test, append the line number: `mix test path/to/test.exs:123`. ```elixir mix test test/my_test.exs:123 ``` -------------------------------- ### PO file output for custom modifier Source: https://github.com/zebbra/gettext_sigils/blob/main/guides/pluralization.md The resulting PO file entries for the custom modifier. ```pot msgid "One error" msgid_plural "%{count} errors" msgstr[0] "One error" msgstr[1] "%{count} errors" ``` -------------------------------- ### Add Gettext Sigils to Mix Dependencies Source: https://github.com/zebbra/gettext_sigils/blob/main/README.md Manually add the gettext_sigils package to your project's dependencies in mix.exs. ```elixir def deps do [ {:gettext_sigils, ">~ 0.5.0"} ] end ``` -------------------------------- ### Gettext Sigil vs. Gettext Macro Source: https://github.com/zebbra/gettext_sigils/blob/main/README.md Compares the usage of the new ~t sigil with the traditional gettext macro for translations. ```elixir # before gettext("Hello, %{name}", name: user.name) # after ~t"Hello, #{user.name}" ``` -------------------------------- ### Configure and Use HTML-Safe Modifier Source: https://github.com/zebbra/gettext_sigils/blob/main/guides/modifiers.md Configure Gettext Sigils to use the `RawModifier` for specific sigils. This ensures that strings translated and processed by this modifier are rendered as safe HTML. ```elixir use GettextSigils, backend: MyApp.Gettext, sigils: [ modifiers: [ r: MyApp.RawModifier ] ] ~t"important"r # => {:safe, "important"} ``` -------------------------------- ### pluralize/2 Callback Specification Source: https://github.com/zebbra/gettext_sigils/blob/main/guides/pluralization.md Defines the expected return values for the pluralize/2 callback and explains the behavior of pluralization within the modifier chain. ```APIDOC ## pluralize/2 Callback ### Description The `pluralize/2` callback determines whether a message should be treated as singular or plural. It processes modifiers in a chain and stops at the first modifier that returns a plural tuple. ### Return Values - **{:ok, {msgid, bindings}}** - Indicates a singular message. The sigil continues the modifier chain and emits `dpgettext/4`. - **{:ok, {msgid, msgid_plural, count, bindings}}** - Indicates a plural message. The sigil emits `dpngettext/6`. - **{:error, reason}** - Raises an `ArgumentError` at compile time. ### Modifier Chain Behavior - The sigil walks the modifier chain left-to-right. - Execution stops at the first modifier that returns a plural tuple. - Subsequent modifiers in the chain will not have their `pluralize/2` callback invoked, though other callbacks like `domain_context/3`, `preprocess/2`, and `postprocess/2` will still execute. ``` -------------------------------- ### Interpolate with ~t sigil Source: https://github.com/zebbra/gettext_sigils/blob/main/guides/interpolation.md Basic usage of the ~t sigil to create Gettext placeholders and bindings. ```elixir ~t"The #{fruit.name} is #{color}" # => gettext("The %{fruit_name} is %{color}", fruit_name: fruit.name, color: color) ``` -------------------------------- ### Tagging and Running Tests in Elixir Source: https://github.com/zebbra/gettext_sigils/blob/main/AGENTS.md Use `@tag` to categorize tests. Run only tests with a specific tag using `mix test --only tag`. ```elixir mix test --only tag ``` -------------------------------- ### Implement Custom Pluralization Logic Source: https://context7.com/zebbra/gettext_sigils/llms.txt Define custom pluralization behavior by implementing the pluralize/2 callback in a modifier module. ```elixir defmodule MyApp.SplitPluralModifier do @moduledoc "Splits msgid on separator into singular|plural forms" use GettextSigils.Modifier @schema NimbleOptions.new!( separator: [ type: :string, default: "|", doc: "Separator between singular and plural forms" ] ) @impl true def init(opts), do: NimbleOptions.validate(opts, @schema) @impl true def pluralize({msgid, bindings}, opts) do case Keyword.pop(bindings, :count) do {nil, _} -> {:error, ~s|modifier requires a "count" binding|} {count, remaining} -> separator = Keyword.fetch!(opts, :separator) case String.split(msgid, separator, parts: 2) do [singular, plural] -> {:ok, {singular, plural, count, remaining}} _ -> {:error, ~s|msgid must be in "singular#{separator}plural" format|} end end end end defmodule MyApp.Messages do use GettextSigils, backend: MyApp.Gettext, sigils: [ modifiers: [ n: MyApp.SplitPluralModifier, p: {MyApp.SplitPluralModifier, separator: " :: "} ] ] def error_count(count) do ~t"One error|#{count} errors"n # => dpngettext("default", nil, "One error", "%{count} errors", count, []) end def user_count(count) do ~t"One user :: #{count} users"p # Uses custom " :: " separator end end ``` -------------------------------- ### Registering custom modifiers Source: https://github.com/zebbra/gettext_sigils/blob/main/guides/pluralization.md Configure custom modifiers in the GettextSigils backend configuration. ```elixir defmodule MyApp.Frontend do use GettextSigils, backend: MyApp.Gettext, sigils: [ modifiers: [ n: MyApp.SplitPluralModifier, p: {MyApp.SplitPluralModifier, separator: " :: "} ] ] def error_count(count), do: ~t"One error|#{count} errors"n def user_count(count), do: ~t"One user :: #{count} users"p end ``` -------------------------------- ### Concurrent Enumeration with Task.async_stream/3 Source: https://github.com/zebbra/gettext_sigils/blob/main/AGENTS.md Employ `Task.async_stream/3` for concurrent processing of enumerables, providing back-pressure. This is useful for managing concurrent operations efficiently. ```elixir Task.async_stream(enumerable, fun, args, options) ``` -------------------------------- ### Configure Sigil Modifiers in Elixir Source: https://context7.com/zebbra/gettext_sigils/llms.txt Define domain, context, and custom modifiers within the GettextSigils configuration. ```elixir defmodule MyAppWeb.Dashboard do use GettextSigils, backend: MyApp.Gettext, sigils: [ domain: "frontend", context: inspect(__MODULE__), modifiers: [ e: [domain: "errors"], # Override domain to "errors" g: [domain: :default, context: nil], # Reset to backend defaults m: [context: inspect(__MODULE__)] # Set explicit context ] ] def examples do # Default: domain "frontend", context "MyAppWeb.Dashboard" ~t"Welcome" # With 'e' modifier: domain "errors", context "MyAppWeb.Dashboard" ~t"Not found"e # With 'g' modifier: backend default domain, no context ~t"Yes"g # Multiple modifiers chain left-to-right # 'e' sets domain to "errors", then 'g' resets to defaults ~t"Oops"eg end end ``` -------------------------------- ### Handle ambiguous keys Source: https://github.com/zebbra/gettext_sigils/blob/main/guides/interpolation.md The sigil raises an ArgumentError at compile time when different expressions map to the same derived key. ```elixir ~t"This is invalid: #{Foo.bar()} != #{foo.bar}" # => ** (ArgumentError) ambiguous interpolation key "foo_bar" ... ``` -------------------------------- ### Runtime String Transformation Source: https://github.com/zebbra/gettext_sigils/blob/main/guides/modifiers.md Modifiers can postprocess the translated string at runtime. ```elixir # a modifier that uppercases the result ~t"hello"u # => String.upcase(dpgettext("default", nil, "hello", [])) # => "HELLO" ``` -------------------------------- ### Configure and Use Markdown Modifier Source: https://github.com/zebbra/gettext_sigils/blob/main/guides/modifiers.md Configure the Markdown modifier with specific MDEx options, such as enabling strikethrough support. The modifier is then applied using its assigned sigil. ```elixir use GettextSigils, backend: MyApp.Gettext, sigils: [ modifiers: [ m: {MyApp.MarkdownModifier, extension: [strikethrough: true]} ] ] ~t"**bold** and ~~struck~~"m # => {:ok, "
bold and struck