From a55c43d1917e93301c6dcda5b9b305d6f5687244 Mon Sep 17 00:00:00 2001
From: Tom Main
Date: Thu, 3 Sep 2026 21:37:49 -0400
Subject: [PATCH 1/4] feat: stabilize Markdown translation units
---
internal/formats/formats.go | 8 ++
internal/formats/markdown.go | 227 +++++++++++++++++++++++++++++-
internal/formats/markdown_test.go | 94 +++++++++++++
internal/llm/provider.go | 4 +-
internal/llm/provider_test.go | 2 +-
internal/policy/policy.go | 33 ++++-
internal/policy/policy_test.go | 8 +-
internal/state/manifest.go | 23 +--
internal/translate/translate.go | 43 ++++--
internal/validate/validate.go | 13 +-
10 files changed, 409 insertions(+), 46 deletions(-)
create mode 100644 internal/formats/markdown_test.go
diff --git a/internal/formats/formats.go b/internal/formats/formats.go
index 7c1b9ba..a69f4ba 100644
--- a/internal/formats/formats.go
+++ b/internal/formats/formats.go
@@ -18,6 +18,14 @@ type Format interface {
Serialize(entries map[string]string, original []byte) ([]byte, error)
}
+// PairedFormat uses source structure when reading and writing a translated
+// target. This lets document formats retain stable semantic unit identities
+// even when headings and prose are translated.
+type PairedFormat interface {
+ ParseTarget(source, target []byte) (map[string]string, error)
+ SerializeTarget(entries map[string]string, source, target []byte) ([]byte, error)
+}
+
// EntryRemover is implemented by structured formats that can remove selected
// string leaves while preserving the rest of the original document shape.
type EntryRemover interface {
diff --git a/internal/formats/markdown.go b/internal/formats/markdown.go
index 775b538..46ae5bb 100644
--- a/internal/formats/markdown.go
+++ b/internal/formats/markdown.go
@@ -1,19 +1,232 @@
package formats
-// MarkdownFormat treats a whole markdown file as a single translation unit.
-// The key is always "_content" and the value is the full document text.
+import (
+ "fmt"
+ "regexp"
+ "strings"
+ "unicode"
+)
+
+const markdownPreambleKey = "markdown:preamble"
+
+var (
+ markdownFencePattern = regexp.MustCompile(`^[ \t]{0,3}(` + "`{3,}" + `|~{3,})`)
+ markdownH2Pattern = regexp.MustCompile(`^[ \t]{0,3}##[ \t]+(.+?)[ \t]*#*[ \t]*(?:\r?\n)?$`)
+ markdownMarkerPattern = regexp.MustCompile(`^[ \t]*[ \t]*(?:\r?\n)?$`)
+)
+
+// MarkdownFormat translates the document preamble and each level-two section
+// independently. Paired serialization writes stable, invisible unit markers
+// into targets so later source-section insertions do not remap existing prose.
type MarkdownFormat struct{}
func (f *MarkdownFormat) Name() string { return "markdown" }
func (f *MarkdownFormat) Extensions() []string { return []string{".md", ".mdx"} }
func (f *MarkdownFormat) Parse(data []byte) (map[string]string, error) {
- return map[string]string{"_content": string(data)}, nil
+ sections, err := parseMarkdownSections(data)
+ if err != nil {
+ return nil, err
+ }
+ assignSourceKeys(sections)
+ return markdownEntries(sections), nil
+}
+
+// ParseTarget maps a translated document to the source's stable section keys.
+// Existing unmarked documents are accepted only when their section count still
+// matches, which provides a safe one-time migration path.
+func (f *MarkdownFormat) ParseTarget(source, target []byte) (map[string]string, error) {
+ sourceSections, err := parseMarkdownSections(source)
+ if err != nil {
+ return nil, fmt.Errorf("parsing markdown source: %w", err)
+ }
+ assignSourceKeys(sourceSections)
+ targetSections, err := parseMarkdownSections(target)
+ if err != nil {
+ return nil, fmt.Errorf("parsing markdown target: %w", err)
+ }
+
+ marked := 0
+ for _, section := range targetSections[1:] {
+ if section.marker != "" {
+ marked++
+ }
+ }
+ if marked != 0 && marked != len(targetSections)-1 {
+ return nil, fmt.Errorf("markdown target mixes marked and unmarked sections")
+ }
+ if marked == 0 {
+ if len(targetSections) != len(sourceSections) {
+ return nil, fmt.Errorf("unmarked markdown target has %d sections; source has %d", len(targetSections)-1, len(sourceSections)-1)
+ }
+ for i := range targetSections {
+ targetSections[i].key = sourceSections[i].key
+ }
+ } else {
+ targetSections[0].key = markdownPreambleKey
+ seen := map[string]struct{}{markdownPreambleKey: {}}
+ for _, section := range targetSections[1:] {
+ if _, duplicate := seen[section.marker]; duplicate {
+ return nil, fmt.Errorf("duplicate markdown unit marker %q", section.marker)
+ }
+ seen[section.marker] = struct{}{}
+ section.key = section.marker
+ }
+ }
+ return markdownEntries(targetSections), nil
+}
+
+func (f *MarkdownFormat) Serialize(entries map[string]string, original []byte) ([]byte, error) {
+ sections, err := parseMarkdownSections(original)
+ if err != nil {
+ return nil, err
+ }
+ assignSourceKeys(sections)
+ return serializeMarkdownSections(entries, sections, false)
+}
+
+// SerializeTarget follows source-section order and emits target-side markers.
+func (f *MarkdownFormat) SerializeTarget(entries map[string]string, source, _ []byte) ([]byte, error) {
+ sections, err := parseMarkdownSections(source)
+ if err != nil {
+ return nil, fmt.Errorf("parsing markdown source: %w", err)
+ }
+ assignSourceKeys(sections)
+ return serializeMarkdownSections(entries, sections, true)
+}
+
+type markdownSection struct {
+ key string
+ marker string
+ heading string
+ value string
}
-func (f *MarkdownFormat) Serialize(entries map[string]string, _ []byte) ([]byte, error) {
- if content, ok := entries["_content"]; ok {
- return []byte(content), nil
+func parseMarkdownSections(data []byte) ([]*markdownSection, error) {
+ sections := []*markdownSection{{key: markdownPreambleKey}}
+ current := sections[0]
+ var value strings.Builder
+ var pendingMarker string
+ var fence byte
+ var fenceLength int
+
+ flush := func() {
+ current.value = value.String()
+ value.Reset()
+ }
+ for _, line := range splitLinesAfter(string(data)) {
+ if match := markdownFencePattern.FindStringSubmatch(line); match != nil {
+ marker := match[1]
+ if fence == 0 {
+ fence = marker[0]
+ fenceLength = len(marker)
+ } else if marker[0] == fence && len(marker) >= fenceLength {
+ fence = 0
+ fenceLength = 0
+ }
+ value.WriteString(line)
+ continue
+ }
+ if fence == 0 {
+ if match := markdownMarkerPattern.FindStringSubmatch(line); match != nil {
+ if pendingMarker != "" {
+ return nil, fmt.Errorf("markdown unit marker %q is not followed by a level-two heading", pendingMarker)
+ }
+ pendingMarker = match[1]
+ continue
+ }
+ if match := markdownH2Pattern.FindStringSubmatch(line); match != nil {
+ flush()
+ current = &markdownSection{marker: pendingMarker, heading: strings.TrimSpace(match[1])}
+ sections = append(sections, current)
+ pendingMarker = ""
+ value.WriteString(line)
+ continue
+ }
+ if pendingMarker != "" {
+ return nil, fmt.Errorf("markdown unit marker %q is not immediately followed by a level-two heading", pendingMarker)
+ }
+ }
+ value.WriteString(line)
+ }
+ if pendingMarker != "" {
+ return nil, fmt.Errorf("markdown unit marker %q has no section", pendingMarker)
+ }
+ flush()
+ return sections, nil
+}
+
+func assignSourceKeys(sections []*markdownSection) {
+ seen := make(map[string]int)
+ sections[0].key = markdownPreambleKey
+ for index, section := range sections[1:] {
+ if section.marker != "" {
+ section.key = section.marker
+ continue
+ }
+ slug := markdownSlug(section.heading)
+ if slug == "" {
+ slug = fmt.Sprintf("section-%d", index+1)
+ }
+ seen[slug]++
+ if seen[slug] > 1 {
+ slug = fmt.Sprintf("%s-%d", slug, seen[slug])
+ }
+ section.key = "markdown:" + slug
+ }
+}
+
+func markdownEntries(sections []*markdownSection) map[string]string {
+ entries := make(map[string]string, len(sections))
+ for _, section := range sections {
+ entries[section.key] = section.value
+ }
+ return entries
+}
+
+func serializeMarkdownSections(entries map[string]string, order []*markdownSection, markers bool) ([]byte, error) {
+ var output strings.Builder
+ for index, section := range order {
+ value, ok := entries[section.key]
+ if !ok {
+ return nil, fmt.Errorf("markdown entry %q is missing", section.key)
+ }
+ if index > 0 && markers {
+ if output.Len() > 0 && !strings.HasSuffix(output.String(), "\n") {
+ output.WriteByte('\n')
+ }
+ fmt.Fprintf(&output, "\n", section.key)
+ }
+ output.WriteString(value)
+ }
+ return []byte(output.String()), nil
+}
+
+func markdownSlug(heading string) string {
+ var slug strings.Builder
+ separator := false
+ for _, r := range strings.ToLower(heading) {
+ switch {
+ case unicode.IsLetter(r) || unicode.IsNumber(r):
+ if separator && slug.Len() > 0 {
+ slug.WriteByte('-')
+ }
+ separator = false
+ slug.WriteRune(r)
+ default:
+ separator = true
+ }
+ }
+ return slug.String()
+}
+
+func splitLinesAfter(value string) []string {
+ if value == "" {
+ return nil
+ }
+ lines := strings.SplitAfter(value, "\n")
+ if lines[len(lines)-1] == "" {
+ return lines[:len(lines)-1]
}
- return nil, nil
+ return lines
}
diff --git a/internal/formats/markdown_test.go b/internal/formats/markdown_test.go
new file mode 100644
index 0000000..71fc7fd
--- /dev/null
+++ b/internal/formats/markdown_test.go
@@ -0,0 +1,94 @@
+package formats
+
+import (
+ "reflect"
+ "strings"
+ "testing"
+)
+
+func TestMarkdownParseKeepsUnchangedSectionsStable(t *testing.T) {
+ format := &MarkdownFormat{}
+ before := []byte("# Product\n\nIntro.\n\n## Install\n\nRun this.\n\n## Usage\n\nUse it.\n")
+ after := []byte("# Product\n\nIntro.\n\n## Install\n\nRun this command.\n\n## Usage\n\nUse it.\n")
+
+ beforeEntries, err := format.Parse(before)
+ if err != nil {
+ t.Fatal(err)
+ }
+ afterEntries, err := format.Parse(after)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if len(beforeEntries) != 3 {
+ t.Fatalf("entry count = %d, want 3", len(beforeEntries))
+ }
+ if _, ok := beforeEntries["markdown:install"]; !ok {
+ t.Fatalf("entries = %#v, want markdown:install", beforeEntries)
+ }
+ if beforeEntries["markdown:preamble"] != afterEntries["markdown:preamble"] {
+ t.Fatal("preamble changed when only the install section changed")
+ }
+ if beforeEntries["markdown:usage"] != afterEntries["markdown:usage"] {
+ t.Fatal("usage changed when only the install section changed")
+ }
+ if beforeEntries["markdown:install"] == afterEntries["markdown:install"] {
+ t.Fatal("install section did not change")
+ }
+}
+
+func TestMarkdownPairedSerializationUsesStableMarkers(t *testing.T) {
+ format := &MarkdownFormat{}
+ source := []byte("# Product\n\nIntro.\n\n## Install\n\nRun this.\n\n## Usage\n\nUse it.\n")
+ target := []byte("# Produit\n\nIntroduction.\n\n## Installation\n\nExécutez ceci.\n\n## Utilisation\n\nUtilisez-le.\n")
+
+ entries, err := format.ParseTarget(source, target)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if got := entries["markdown:install"]; !strings.Contains(got, "Installation") {
+ t.Fatalf("install target = %q", got)
+ }
+
+ output, err := format.SerializeTarget(entries, source, target)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !strings.Contains(string(output), "\n## Installation") {
+ t.Fatalf("output lacks stable install marker:\n%s", output)
+ }
+
+ reparsed, err := format.ParseTarget(source, output)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !reflect.DeepEqual(reparsed, entries) {
+ t.Fatalf("reparsed = %#v, want %#v", reparsed, entries)
+ }
+}
+
+func TestMarkdownMarkersSurviveSourceSectionInsertion(t *testing.T) {
+ format := &MarkdownFormat{}
+ before := []byte("# Product\n\n## Install\n\nRun this.\n\n## Usage\n\nUse it.\n")
+ target := []byte("# Produit\n\n## Installation\n\nExécutez ceci.\n\n## Utilisation\n\nUtilisez-le.\n")
+ entries, err := format.ParseTarget(before, target)
+ if err != nil {
+ t.Fatal(err)
+ }
+ marked, err := format.SerializeTarget(entries, before, target)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ after := []byte("# Product\n\n## Install\n\nRun this.\n\n## Configuration\n\nConfigure it.\n\n## Usage\n\nUse it.\n")
+ afterEntries, err := format.ParseTarget(after, marked)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if _, ok := afterEntries["markdown:configuration"]; ok {
+ t.Fatal("new source section unexpectedly has a target")
+ }
+ if got := afterEntries["markdown:usage"]; !strings.Contains(got, "Utilisation") {
+ t.Fatalf("usage target lost after insertion: %q", got)
+ }
+}
diff --git a/internal/llm/provider.go b/internal/llm/provider.go
index 6e230ee..7cf1225 100644
--- a/internal/llm/provider.go
+++ b/internal/llm/provider.go
@@ -141,8 +141,8 @@ func BuildDocumentPrompt(sourceLocale, targetLocale, styleGuide string, terms []
b.WriteString("- Preserve all Markdown formatting (headings, links, code blocks, lists).\n")
b.WriteString("- Preserve interpolation variables exactly: {{variable}}, {variable}, %{variable}.\n")
b.WriteString("- Do not translate code blocks or inline code.\n")
- b.WriteString("- Return a JSON object with the original _content key mapped to the translated document.\n")
- b.WriteString("- Do not add commentary or keys other than _content.\n")
+ b.WriteString("- Return a JSON object with every original input key mapped to its translated document unit.\n")
+ b.WriteString("- Do not add commentary, omit keys, or add keys that were not present in the input.\n")
b.WriteString("- Keep brand names and technical terms in English unless the glossary specifies otherwise.\n")
if len(terms) > 0 {
diff --git a/internal/llm/provider_test.go b/internal/llm/provider_test.go
index 2bdac9f..46abcbd 100644
--- a/internal/llm/provider_test.go
+++ b/internal/llm/provider_test.go
@@ -9,7 +9,7 @@ import (
func TestBuildDocumentPromptKeepsProviderJSONContract(t *testing.T) {
prompt := BuildDocumentPrompt("en", "fr", "", nil)
- if !strings.Contains(prompt, "_content") || !strings.Contains(prompt, "JSON object") {
+ if !strings.Contains(prompt, "every original input key") || !strings.Contains(prompt, "JSON object") {
t.Fatalf("document prompt does not preserve the provider JSON contract: %q", prompt)
}
}
diff --git a/internal/policy/policy.go b/internal/policy/policy.go
index 84e72ae..3f30c67 100644
--- a/internal/policy/policy.go
+++ b/internal/policy/policy.go
@@ -9,14 +9,17 @@ import (
"github.com/Tom-R-Main/Internationalizer/internal/state"
)
-const promptPolicyVersion = 1
+const PromptContractVersion = 2
// Resolved is the resolved prompt, provider settings, and stable hash for
// one target locale and source format.
type Resolved struct {
- Prompt string
- LLM config.LLM
- Hash string
+ Prompt string
+ LLM config.LLM
+ Hash string
+ GuideHash string
+ GlossaryHash string
+ PromptVersion int
}
// Resolve builds the effective translation policy. Callers provide
@@ -42,6 +45,14 @@ func Resolve(cfg *config.Config, targetLocale, format, styleGuide string, terms
prompt = llm.BuildDocumentPrompt(canonicalSource, canonicalTarget, styleGuide, terms)
}
+ guideHash, err := state.HashValue(styleGuide)
+ if err != nil {
+ return Resolved{}, err
+ }
+ glossaryHash, err := state.HashValue(terms)
+ if err != nil {
+ return Resolved{}, err
+ }
hash, err := state.HashValue(struct {
Version int `json:"version"`
SourceLocale string `json:"source_locale"`
@@ -50,11 +61,19 @@ func Resolve(cfg *config.Config, targetLocale, format, styleGuide string, terms
Provider string `json:"provider"`
Model string `json:"model"`
Reasoning string `json:"reasoning_effort"`
- Prompt string `json:"prompt"`
- }{promptPolicyVersion, canonicalSource, canonicalTarget, format, effectiveLLM.Provider, effectiveLLM.Model, llm.EffectiveReasoningEffort(effectiveLLM), prompt})
+ GuideHash string `json:"guide_hash"`
+ GlossaryHash string `json:"glossary_hash"`
+ }{PromptContractVersion, canonicalSource, canonicalTarget, format, effectiveLLM.Provider, effectiveLLM.Model, llm.EffectiveReasoningEffort(effectiveLLM), guideHash, glossaryHash})
if err != nil {
return Resolved{}, err
}
- return Resolved{Prompt: prompt, LLM: effectiveLLM, Hash: hash}, nil
+ return Resolved{
+ Prompt: prompt,
+ LLM: effectiveLLM,
+ Hash: hash,
+ GuideHash: guideHash,
+ GlossaryHash: glossaryHash,
+ PromptVersion: PromptContractVersion,
+ }, nil
}
diff --git a/internal/policy/policy_test.go b/internal/policy/policy_test.go
index e602acc..8a78dff 100644
--- a/internal/policy/policy_test.go
+++ b/internal/policy/policy_test.go
@@ -28,9 +28,11 @@ func TestResolveIsDeterministic(t *testing.T) {
if want := cfg.LLMForLocale("fr"); !reflect.DeepEqual(first.LLM, want) {
t.Fatalf("effective LLM = %#v, want %#v", first.LLM, want)
}
- const wantHash = "fdfbfa87db81fd2fe792922f343342fe89705e51bb8bee61534a6813f17bf3d1"
- if first.Hash != wantHash {
- t.Fatalf("hash = %q, want legacy-compatible %q", first.Hash, wantHash)
+ if first.Hash == "" || first.GuideHash == "" || first.GlossaryHash == "" {
+ t.Fatalf("resolution lacks component hashes: %#v", first)
+ }
+ if first.PromptVersion != policy.PromptContractVersion {
+ t.Fatalf("prompt version = %d, want %d", first.PromptVersion, policy.PromptContractVersion)
}
}
diff --git a/internal/state/manifest.go b/internal/state/manifest.go
index a931405..ff89270 100644
--- a/internal/state/manifest.go
+++ b/internal/state/manifest.go
@@ -23,16 +23,19 @@ type Manifest struct {
// Entry records the inputs and output for one bundle key and target locale.
type Entry struct {
- Bundle string `json:"bundle"`
- Key string `json:"key"`
- Locale string `json:"locale"`
- SourceHash string `json:"source_hash"`
- PolicyHash string `json:"policy_hash"`
- TargetHash string `json:"target_hash"`
- Origin string `json:"origin,omitempty"`
- Provider string `json:"provider,omitempty"`
- Model string `json:"model,omitempty"`
- UpdatedAt time.Time `json:"updated_at"`
+ Bundle string `json:"bundle"`
+ Key string `json:"key"`
+ Locale string `json:"locale"`
+ SourceHash string `json:"source_hash"`
+ PolicyHash string `json:"policy_hash"`
+ GuideHash string `json:"guide_hash,omitempty"`
+ GlossaryHash string `json:"glossary_hash,omitempty"`
+ PromptVersion int `json:"prompt_version,omitempty"`
+ TargetHash string `json:"target_hash"`
+ Origin string `json:"origin,omitempty"`
+ Provider string `json:"provider,omitempty"`
+ Model string `json:"model,omitempty"`
+ UpdatedAt time.Time `json:"updated_at"`
}
// New returns an empty manifest using the current schema.
diff --git a/internal/translate/translate.go b/internal/translate/translate.go
index 0b100b0..f5c41c7 100644
--- a/internal/translate/translate.go
+++ b/internal/translate/translate.go
@@ -298,7 +298,7 @@ func translateLocale(
case err == nil:
targetExists = true
targetData = data
- targetKeys, err = bundle.format.Parse(data)
+ targetKeys, err = parseTarget(bundle.format, bundle.sourceData, data)
if err != nil {
result.Errors = append(result.Errors, fmt.Sprintf("parsing target %s: %v", targetPath, err))
return jobOutput{result: result}
@@ -442,13 +442,13 @@ func translateLocale(
}
}
}
- output, err := bundle.format.Serialize(staged, serializationBaseline)
+ output, err := serializeTarget(bundle.format, staged, bundle.sourceData, serializationBaseline)
if err != nil {
result.Errors = append(result.Errors, fmt.Sprintf("serializing target %s: %v", targetPath, err))
return jobOutput{result: result}
}
output = appendOneNewline(output)
- parsed, err := bundle.format.Parse(output)
+ parsed, err := parseTarget(bundle.format, bundle.sourceData, output)
if err != nil {
result.Errors = append(result.Errors, fmt.Sprintf("validating staged target %s: %v", targetPath, err))
return jobOutput{result: result}
@@ -488,22 +488,39 @@ func translateLocale(
continue
}
updates = append(updates, state.Entry{
- Bundle: bundle.bundle.ID,
- Key: plan.key,
- Locale: locale,
- SourceHash: plan.sourceHash,
- PolicyHash: policyHash,
- TargetHash: state.TargetHash(targetValue),
- Origin: origin.kind,
- Provider: origin.provider,
- Model: origin.model,
- UpdatedAt: now,
+ Bundle: bundle.bundle.ID,
+ Key: plan.key,
+ Locale: locale,
+ SourceHash: plan.sourceHash,
+ PolicyHash: policyHash,
+ GuideHash: translationPolicy.GuideHash,
+ GlossaryHash: translationPolicy.GlossaryHash,
+ PromptVersion: translationPolicy.PromptVersion,
+ TargetHash: state.TargetHash(targetValue),
+ Origin: origin.kind,
+ Provider: origin.provider,
+ Model: origin.model,
+ UpdatedAt: now,
})
}
}
return jobOutput{result: result, updates: updates}
}
+func parseTarget(format formats.Format, source, target []byte) (map[string]string, error) {
+ if paired, ok := format.(formats.PairedFormat); ok {
+ return paired.ParseTarget(source, target)
+ }
+ return format.Parse(target)
+}
+
+func serializeTarget(format formats.Format, entries map[string]string, source, target []byte) ([]byte, error) {
+ if paired, ok := format.(formats.PairedFormat); ok {
+ return paired.SerializeTarget(entries, source, target)
+ }
+ return format.Serialize(entries, target)
+}
+
type entryState struct {
missing bool
sourceStale bool
diff --git a/internal/validate/validate.go b/internal/validate/validate.go
index 54714ab..75d0b6f 100644
--- a/internal/validate/validate.go
+++ b/internal/validate/validate.go
@@ -113,7 +113,7 @@ func ValidateWithOptions(cfg *config.Config, opts Options) ([]Report, error) {
policyHash = resolved.Hash
}
- report := validateLocale(bundle.ID, cfg.SourceLocale, locale, sourceKeys, targetPath, format, terms, manifest, policyHash, cfg.Validation.PluralStyle, opts)
+ report := validateLocale(bundle.ID, cfg.SourceLocale, locale, sourceData, sourceKeys, targetPath, format, terms, manifest, policyHash, cfg.Validation.PluralStyle, opts)
reports = append(reports, report)
}
}
@@ -127,7 +127,7 @@ func formatForBundle(bundle config.Bundle) (formats.Format, error) {
return formats.FormatForFile(bundle.Source)
}
-func validateLocale(bundle, sourceLocale, locale string, sourceKeys map[string]string, targetPath string, format formats.Format, terms []glossary.Term, manifest *state.Manifest, policyHash, pluralStyle string, opts Options) Report {
+func validateLocale(bundle, sourceLocale, locale string, sourceData []byte, sourceKeys map[string]string, targetPath string, format formats.Format, terms []glossary.Term, manifest *state.Manifest, policyHash, pluralStyle string, opts Options) Report {
report := Report{Bundle: bundle, Locale: locale, TargetPath: targetPath}
if opts.Strict {
translated := 0.0
@@ -161,7 +161,7 @@ func validateLocale(bundle, sourceLocale, locale string, sourceKeys map[string]s
return report
}
- targetKeys, err := format.Parse(targetData)
+ targetKeys, err := parseTarget(format, sourceData, targetData)
if err != nil {
report.Missing = allKeys(validationKeys)
report.Errors = append(report.Errors, fmt.Sprintf("parsing target: %v", err))
@@ -250,6 +250,13 @@ func validateLocale(bundle, sourceLocale, locale string, sourceKeys map[string]s
return report
}
+func parseTarget(format formats.Format, source, target []byte) (map[string]string, error) {
+ if paired, ok := format.(formats.PairedFormat); ok {
+ return paired.ParseTarget(source, target)
+ }
+ return format.Parse(target)
+}
+
// ICUFindings compares ICU MessageFormat structure independently of linguistic
// strictness. Message syntax and branch identity are runtime correctness.
func ICUFindings(key, source, target, targetLocale string) []Finding {
From 29f0a74deaf4fe3b7a9d0ed1c1961b0eb861b0de Mon Sep 17 00:00:00 2001
From: Tom Main
Date: Thu, 3 Sep 2026 21:38:14 -0400
Subject: [PATCH 2/4] fix: restore English links in Chinese READMEs
---
docs/i18n/zh-CN.md | 2 ++
docs/i18n/zh-TW.md | 2 ++
2 files changed, 4 insertions(+)
diff --git a/docs/i18n/zh-CN.md b/docs/i18n/zh-CN.md
index 1ea86bf..f96d4cc 100644
--- a/docs/i18n/zh-CN.md
+++ b/docs/i18n/zh-CN.md
@@ -1,3 +1,5 @@
+> [English (original)](../../README.md)
+
diff --git a/docs/i18n/zh-TW.md b/docs/i18n/zh-TW.md
index 6b504e9..074f23f 100644
--- a/docs/i18n/zh-TW.md
+++ b/docs/i18n/zh-TW.md
@@ -1,3 +1,5 @@
+> [English (original)](../../README.md)
+
From 8f11f6e8390035bcc41e1de2935e6a810bd23483 Mon Sep 17 00:00:00 2001
From: Tom Main
Date: Thu, 3 Sep 2026 22:22:46 -0400
Subject: [PATCH 3/4] feat: protect durable translation policy
---
.github/CODEOWNERS | 3 +
README.md | 16 +++-
docs/localization-core-v2.md | 20 +++--
internal/tm/tm.go | 23 +++---
internal/tm/tm_test.go | 22 ++++--
internal/translate/protected.go | 21 +++++-
internal/translate/translate.go | 46 +++++++-----
internal/translate/translate_test.go | 102 ++++++++++++++++++++++++-
internal/validate/protected.go | 108 ++++++++++++++++++++++++---
internal/validate/strict_test.go | 32 ++++++++
internal/validate/validate.go | 18 +++--
scripts/generate-style-guides.mjs | 25 ++++++-
style-guides/README.md | 39 ++++++++++
13 files changed, 408 insertions(+), 67 deletions(-)
create mode 100644 .github/CODEOWNERS
create mode 100644 style-guides/README.md
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 0000000..f7fd06e
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1,3 @@
+# Translation policy and localized public documentation require owner review.
+/style-guides/ @Tom-R-Main
+/docs/i18n/ @Tom-R-Main
diff --git a/README.md b/README.md
index 442f7a3..96d1c4f 100644
--- a/README.md
+++ b/README.md
@@ -339,6 +339,13 @@ Define rules that apply to all languages: interpolation syntax, HTML preservatio
Define language-specific rules: formality register (tu vs. vous), punctuation (guillemets, inverted question marks), plural forms, date/number formatting, and a terminology glossary.
+Style guides are durable policy inputs, not generated output. Internationalizer
+reads them but never rewrites them. Their content is hashed separately from the
+glossary and prompt contract, so an application code change does not make a
+translation stale. Editing a guide intentionally marks that locale for policy
+review; changing internal prompt wording does not, unless the prompt contract
+version also changes.
+
See [`examples/react-app/style-guides/`](examples/react-app/style-guides/) for a working example.
## Glossary Format
@@ -370,7 +377,7 @@ findings; it does not exempt a longer value merely containing `API`.
Translation memory is stored as a JSONL file (one JSON record per line). Each record contains:
- The bundle, key, source value, translated value, and canonical target locale
-- Source and translation-policy hashes
+- Source, style-guide, glossary, prompt-contract, and combined policy hashes
- The provider and model that produced the translation
- A timestamp
@@ -386,7 +393,12 @@ reviewable `.internationalizer.lock` manifest is versioned separately.
|--------|-----------|------|
| JSON | `.json` | Key-value (nested, dot-notation flattened) |
| YAML | `.yml`, `.yaml` | Key-value (preserves comments and ordering) |
-| Markdown | `.md`, `.mdx` | Whole-document translation |
+| Markdown | `.md`, `.mdx` | Preamble and H2-level sections |
+
+Markdown targets contain invisible `internationalizer:unit` comments before
+H2 sections. These stable markers let Internationalizer add, move, or edit one
+source section without retranslating unrelated sections. Existing unmarked
+documents receive markers on their next successful update.
## Project Type Detection
diff --git a/docs/localization-core-v2.md b/docs/localization-core-v2.md
index a300375..1a3a816 100644
--- a/docs/localization-core-v2.md
+++ b/docs/localization-core-v2.md
@@ -31,15 +31,25 @@ framework runtime, translation CDN, or source-rewriting setup command.
## Review transfer
-Manifest v2 will represent generated, needs-review, approved, and manual
-origins explicitly. Only an exact source-and-policy match may inherit approval.
-Fuzzy candidates, changed policy, or changed message structure require review.
+Only an exact source-and-policy match may inherit review state. Fuzzy
+candidates, changed policy, or changed message structure require review.
## Source units
-Structured documents and source adapters will identify a unit by adapter,
+Structured documents and source adapters identify a unit by adapter,
normalized project-relative path, semantic location, and source structure.
-Formatting offsets alone are not stable identity.
+Formatting offsets alone are not stable identity. Markdown uses the preamble
+and each H2 section as independent units, with invisible target-side markers to
+preserve identity when sections move or are inserted.
+
+## Policy identity
+
+The manifest records style-guide, glossary, and prompt-contract components
+separately. The combined policy hash includes those components and the provider
+settings, but not the rendered prompt text. Refactoring prompt construction
+therefore leaves current translations alone; a semantic prompt change requires
+an explicit prompt-contract version bump. Style guides are read-only inputs and
+are never generated or rewritten by a translation run.
## Compatibility boundary
diff --git a/internal/tm/tm.go b/internal/tm/tm.go
index 48eb151..1c00638 100644
--- a/internal/tm/tm.go
+++ b/internal/tm/tm.go
@@ -16,16 +16,19 @@ import (
// Record is a single translation memory entry.
type Record struct {
- Bundle string `json:"bundle,omitempty"`
- Key string `json:"key"`
- Source string `json:"source"`
- Target string `json:"target"`
- Locale string `json:"locale"`
- Hash string `json:"hash"`
- PolicyHash string `json:"policy_hash,omitempty"`
- Provider string `json:"provider,omitempty"`
- Model string `json:"model,omitempty"`
- Timestamp time.Time `json:"timestamp"`
+ Bundle string `json:"bundle,omitempty"`
+ Key string `json:"key"`
+ Source string `json:"source"`
+ Target string `json:"target"`
+ Locale string `json:"locale"`
+ Hash string `json:"hash"`
+ PolicyHash string `json:"policy_hash,omitempty"`
+ GuideHash string `json:"guide_hash,omitempty"`
+ GlossaryHash string `json:"glossary_hash,omitempty"`
+ PromptVersion int `json:"prompt_version,omitempty"`
+ Provider string `json:"provider,omitempty"`
+ Model string `json:"model,omitempty"`
+ Timestamp time.Time `json:"timestamp"`
}
// Stats holds translation memory statistics.
diff --git a/internal/tm/tm_test.go b/internal/tm/tm_test.go
index e53c9c6..84c77ea 100644
--- a/internal/tm/tm_test.go
+++ b/internal/tm/tm_test.go
@@ -24,14 +24,17 @@ func TestTMRoundTrip(t *testing.T) {
// Add a record.
rec := Record{
- Bundle: "app",
- Key: "common.save",
- Source: "Save",
- Target: "Enregistrer",
- Locale: "fr",
- Hash: HashSource("Save"),
- PolicyHash: "policy-v1",
- Timestamp: time.Now(),
+ Bundle: "app",
+ Key: "common.save",
+ Source: "Save",
+ Target: "Enregistrer",
+ Locale: "fr",
+ Hash: HashSource("Save"),
+ PolicyHash: "policy-v1",
+ GuideHash: "guide-v1",
+ GlossaryHash: "glossary-v1",
+ PromptVersion: 2,
+ Timestamp: time.Now(),
}
if err := memory.Add(rec); err != nil {
t.Fatalf("Add: %v", err)
@@ -45,6 +48,9 @@ func TestTMRoundTrip(t *testing.T) {
if lookup.Target != "Enregistrer" {
t.Errorf("got %q, want %q", lookup.Target, "Enregistrer")
}
+ if lookup.GuideHash != "guide-v1" || lookup.GlossaryHash != "glossary-v1" || lookup.PromptVersion != 2 {
+ t.Fatalf("component policy metadata did not round-trip: %#v", lookup)
+ }
// Lookup with different hash should miss.
_, ok = memory.Lookup("fr", "app", "common.save", HashSource("Save changed"), "policy-v1")
diff --git a/internal/translate/protected.go b/internal/translate/protected.go
index b3914bb..fd9cb4a 100644
--- a/internal/translate/protected.go
+++ b/internal/translate/protected.go
@@ -11,11 +11,28 @@ import (
// and explicit-adoption boundaries. Instructions in a prompt are not proof
// that protected source structure survived translation.
func validateTranslationValue(key, source, target, targetLocale string) error {
+ return validateTranslationValueWithContext(key, source, target, targetLocale, valueValidationContext{})
+}
+
+type valueValidationContext struct {
+ document bool
+ sourcePath string
+ targetPath string
+}
+
+func validateTranslationValueWithContext(key, source, target, targetLocale string, validationContext valueValidationContext) error {
if strings.TrimSpace(source) != "" && strings.TrimSpace(target) == "" {
return fmt.Errorf("blank translation for %q", key)
}
- if findings := validation.ProtectedFindings(key, source, target, targetLocale); len(findings) > 0 {
- return fmt.Errorf("%s for %q", findings[0].Message, key)
+ protected := validation.ProtectedFindings(key, source, target, targetLocale)
+ if validationContext.document {
+ protected = validation.ProtectedDocumentFindings(key, source, target, targetLocale, validationContext.sourcePath, validationContext.targetPath)
+ }
+ if len(protected) > 0 {
+ return fmt.Errorf("%s for %q", protected[0].Message, key)
+ }
+ if validationContext.document {
+ return nil
}
if findings := validation.ICUFindings(key, source, target, targetLocale); len(findings) > 0 {
return fmt.Errorf("%s for %q", findings[0].Message, key)
diff --git a/internal/translate/translate.go b/internal/translate/translate.go
index f5c41c7..5c8dd90 100644
--- a/internal/translate/translate.go
+++ b/internal/translate/translate.go
@@ -256,9 +256,11 @@ func translateLocale(
}
}
result := Result{Bundle: bundle.bundle.ID, Locale: locale, KeysTotal: len(sourceKeys)}
- for _, key := range sortedKeys(sourceKeys) {
- if findings := validation.ICUSourceFindings(key, sourceKeys[key], cfg.SourceLocale); len(findings) > 0 {
- result.Errors = append(result.Errors, fmt.Sprintf("source %q: %s", key, findings[0].Message))
+ if bundle.format.Name() != "markdown" {
+ for _, key := range sortedKeys(sourceKeys) {
+ if findings := validation.ICUSourceFindings(key, sourceKeys[key], cfg.SourceLocale); len(findings) > 0 {
+ result.Errors = append(result.Errors, fmt.Sprintf("source %q: %s", key, findings[0].Message))
+ }
}
}
if len(result.Errors) > 0 {
@@ -270,6 +272,11 @@ func translateLocale(
return jobOutput{result: result}
}
result.TargetPath = targetPath
+ validationContext := valueValidationContext{
+ document: bundle.format.Name() == "markdown",
+ sourcePath: bundle.bundle.Source,
+ targetPath: targetPath,
+ }
terms, err := glossary.Load(cfg.GlossaryDir, locale)
if err != nil {
@@ -325,7 +332,7 @@ func translateLocale(
if !plan.state.adoptable() {
continue
}
- if err := validateTranslationValue(plan.key, plan.source, targetKeys[plan.key], locale); err != nil {
+ if err := validateTranslationValueWithContext(plan.key, plan.source, targetKeys[plan.key], locale, validationContext); err != nil {
result.Errors = append(result.Errors, fmt.Sprintf("cannot adopt %q: %v", plan.key, err))
}
}
@@ -360,7 +367,7 @@ func translateLocale(
toTranslate := make([]plannedEntry, 0, len(candidates))
for _, plan := range candidates {
if record, ok := memory.Lookup(locale, bundle.bundle.ID, plan.key, plan.sourceHash, policyHash); ok {
- if err := validateTranslationValue(plan.key, plan.source, record.Target, locale); err == nil {
+ if err := validateTranslationValueWithContext(plan.key, plan.source, record.Target, locale, validationContext); err == nil {
staged[plan.key] = record.Target
origins[plan.key] = translationOrigin{kind: "tm", provider: record.Provider, model: record.Model}
result.KeysCached++
@@ -397,7 +404,7 @@ func translateLocale(
result.Errors = append(result.Errors, fmt.Sprintf("batch %d: provider returned no response", i/batchSize+1))
return jobOutput{result: result}
}
- if err := validateBatch(entries, response.Translations, locale); err != nil {
+ if err := validateBatch(entries, response.Translations, locale, validationContext); err != nil {
result.Errors = append(result.Errors, fmt.Sprintf("batch %d: %v", i/batchSize+1, err))
return jobOutput{result: result}
}
@@ -410,16 +417,19 @@ func translateLocale(
origins[plan.key] = translationOrigin{kind: "provider", provider: provider.Name(), model: localeLLM.Model}
result.KeysTranslated++
records = append(records, tm.Record{
- Bundle: bundle.bundle.ID,
- Key: plan.key,
- Source: plan.source,
- Target: translation,
- Locale: locale,
- Hash: plan.sourceHash,
- PolicyHash: policyHash,
- Provider: provider.Name(),
- Model: localeLLM.Model,
- Timestamp: time.Now().UTC(),
+ Bundle: bundle.bundle.ID,
+ Key: plan.key,
+ Source: plan.source,
+ Target: translation,
+ Locale: locale,
+ Hash: plan.sourceHash,
+ PolicyHash: policyHash,
+ GuideHash: translationPolicy.GuideHash,
+ GlossaryHash: translationPolicy.GlossaryHash,
+ PromptVersion: translationPolicy.PromptVersion,
+ Provider: provider.Name(),
+ Model: localeLLM.Model,
+ Timestamp: time.Now().UTC(),
})
}
}
@@ -584,7 +594,7 @@ func (r *Result) addState(state entryState) {
}
}
-func validateBatch(entries []llm.Entry, translations map[string]string, targetLocale string) error {
+func validateBatch(entries []llm.Entry, translations map[string]string, targetLocale string, validationContext valueValidationContext) error {
expected := make(map[string]struct{}, len(entries))
var missing []string
for _, entry := range entries {
@@ -601,7 +611,7 @@ func validateBatch(entries []llm.Entry, translations map[string]string, targetLo
}
if len(missing) == 0 && len(extra) == 0 {
for _, entry := range entries {
- if err := validateTranslationValue(entry.Key, entry.Value, translations[entry.Key], targetLocale); err != nil {
+ if err := validateTranslationValueWithContext(entry.Key, entry.Value, translations[entry.Key], targetLocale, validationContext); err != nil {
return err
}
}
diff --git a/internal/translate/translate_test.go b/internal/translate/translate_test.go
index 32bd5cf..100280d 100644
--- a/internal/translate/translate_test.go
+++ b/internal/translate/translate_test.go
@@ -20,6 +20,7 @@ type fakeProvider struct {
name string
response *llm.TranslateResponse
calls int
+ requests []llm.TranslateRequest
}
func (p *fakeProvider) Name() string {
@@ -29,8 +30,9 @@ func (p *fakeProvider) Name() string {
return "fake"
}
-func (p *fakeProvider) Translate(_ context.Context, _ llm.TranslateRequest) (*llm.TranslateResponse, error) {
+func (p *fakeProvider) Translate(_ context.Context, request llm.TranslateRequest) (*llm.TranslateResponse, error) {
p.calls++
+ p.requests = append(p.requests, request)
return p.response, nil
}
@@ -430,6 +432,104 @@ func TestRunUsesBundleTargetTemplateAndAdoptsExistingTranslation(t *testing.T) {
}
}
+func TestRunAdoptsMarkdownWithRebasedLinksAndCodeBraces(t *testing.T) {
+ dir := t.TempDir()
+ sourcePath := filepath.Join(dir, "README.md")
+ targetPath := filepath.Join(dir, "docs", "i18n", "fr.md")
+ source := "# Project\n\n[License](LICENSE)\n\n## Configuration\n\n```json\n{\"target\":\"{locale}\"}\n```\n"
+ target := "> [English](../../README.md)\n\n# Projet\n\n[Licence](../../LICENSE)\n\n## Configuration\n\n```json\n{\"target\":\"{locale}\"}\n```\n"
+ if err := os.WriteFile(sourcePath, []byte(source), 0o644); err != nil {
+ t.Fatal(err)
+ }
+ if err := os.MkdirAll(filepath.Dir(targetPath), 0o755); err != nil {
+ t.Fatal(err)
+ }
+ if err := os.WriteFile(targetPath, []byte(target), 0o644); err != nil {
+ t.Fatal(err)
+ }
+ cfg := testConfig(dir, "")
+ cfg.Bundles = []config.Bundle{{
+ ID: "docs",
+ Source: sourcePath,
+ Target: filepath.Join(dir, "docs", "i18n", "{locale}.md"),
+ Format: "markdown",
+ }}
+
+ results, err := Run(context.Background(), cfg, nil, Options{AdoptExisting: true})
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(results) != 1 || len(results[0].Errors) != 0 || results[0].KeysUntracked != 2 {
+ t.Fatalf("results = %#v, want two adopted Markdown units", results)
+ }
+}
+
+func TestRunRetranslatesOnlyChangedMarkdownSectionAndNeverWritesGuide(t *testing.T) {
+ dir := t.TempDir()
+ sourcePath := filepath.Join(dir, "README.md")
+ targetPath := filepath.Join(dir, "docs", "i18n", "fr.md")
+ guidePath := filepath.Join(dir, "style-guides", "fr.md")
+ if err := os.MkdirAll(filepath.Dir(targetPath), 0o755); err != nil {
+ t.Fatal(err)
+ }
+ if err := os.MkdirAll(filepath.Dir(guidePath), 0o755); err != nil {
+ t.Fatal(err)
+ }
+ if err := os.WriteFile(sourcePath, []byte("# Project\n\n## Install\n\nOld install.\n\n## Usage\n\nStable usage.\n"), 0o644); err != nil {
+ t.Fatal(err)
+ }
+ if err := os.WriteFile(targetPath, []byte("# Projet\n\n## Installation\n\nAncienne installation.\n\n## Utilisation\n\nUtilisation stable.\n"), 0o644); err != nil {
+ t.Fatal(err)
+ }
+ guide := []byte("Use clear, professional French.\n")
+ if err := os.WriteFile(guidePath, guide, 0o644); err != nil {
+ t.Fatal(err)
+ }
+
+ cfg := testConfig(dir, "")
+ cfg.Bundles = []config.Bundle{{
+ ID: "docs",
+ Source: sourcePath,
+ Target: filepath.Join(dir, "docs", "i18n", "{locale}.md"),
+ Format: "markdown",
+ }}
+ if _, err := Run(context.Background(), cfg, nil, Options{AdoptExisting: true}); err != nil {
+ t.Fatalf("adopting existing translation: %v", err)
+ }
+ if err := os.WriteFile(sourcePath, []byte("# Project\n\n## Install\n\nNew install.\n\n## Usage\n\nStable usage.\n"), 0o644); err != nil {
+ t.Fatal(err)
+ }
+ provider := &fakeProvider{response: &llm.TranslateResponse{Translations: map[string]string{
+ "markdown:install": "## Installation\n\nNouvelle installation.\n",
+ }}}
+
+ results, err := Run(context.Background(), cfg, provider, Options{})
+ if err != nil {
+ t.Fatal(err)
+ }
+ if results[0].KeysSourceStale != 1 || results[0].KeysTranslated != 1 || provider.calls != 1 {
+ t.Fatalf("results = %#v, provider calls = %d", results, provider.calls)
+ }
+ if len(provider.requests) != 1 || len(provider.requests[0].Entries) != 1 || provider.requests[0].Entries[0].Key != "markdown:install" {
+ t.Fatalf("provider requests = %#v, want only markdown:install", provider.requests)
+ }
+ got, err := os.ReadFile(targetPath)
+ if err != nil {
+ t.Fatal(err)
+ }
+ want := "# Projet\n\n\n## Installation\n\nNouvelle installation.\n\n## Utilisation\n\nUtilisation stable.\n"
+ if string(got) != want {
+ t.Fatalf("target = %q, want %q", got, want)
+ }
+ guideAfter, err := os.ReadFile(guidePath)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if string(guideAfter) != string(guide) {
+ t.Fatalf("style guide changed: got %q, want %q", guideAfter, guide)
+ }
+}
+
func TestAdoptExistingRejectsInterpolationMismatch(t *testing.T) {
dir := t.TempDir()
sourcePath := filepath.Join(dir, "en.json")
diff --git a/internal/validate/protected.go b/internal/validate/protected.go
index 27a3c5c..1a20495 100644
--- a/internal/validate/protected.go
+++ b/internal/validate/protected.go
@@ -2,6 +2,7 @@ package validate
import (
"fmt"
+ "path/filepath"
"regexp"
"strings"
@@ -9,33 +10,60 @@ import (
)
var (
- htmlTagRe = regexp.MustCompile(`(?s)|?[A-Za-z][^>]*>`)
- inlineCodeRe = regexp.MustCompile("`+[^`\\n]*`+")
+ htmlTagRe = regexp.MustCompile(`(?s)|?[A-Za-z][^>]*>`)
+ htmlPathAttrRe = regexp.MustCompile(`(?i)\b(href|src)\s*=\s*("[^"]*"|'[^']*')`)
+ inlineCodeRe = regexp.MustCompile("`+[^`\\n]*`+")
+ uriSchemeRe = regexp.MustCompile(`^[A-Za-z][A-Za-z0-9+.-]*:`)
)
// ProtectedFindings compares source and target structures that translations
// must preserve exactly. Multiple damaged structures may yield multiple
// findings with the same stable code and distinct messages.
func ProtectedFindings(key, source, target, targetLocale string) []Finding {
+ return protectedFindings(key, source, target, targetLocale, "", "")
+}
+
+// ProtectedDocumentFindings compares Markdown document structure while
+// resolving relative links from each document's own directory. A localized
+// document may add one link back to its source.
+func ProtectedDocumentFindings(key, source, target, targetLocale, sourcePath, targetPath string) []Finding {
+ return protectedFindings(key, source, target, targetLocale, sourcePath, targetPath)
+}
+
+func protectedFindings(key, source, target, targetLocale, sourcePath, targetPath string) []Finding {
var findings []Finding
- icu := usesICUValidation(source, target)
+ document := sourcePath != "" && targetPath != ""
+ icu := false
+ if !document {
+ icu = usesICUValidation(source, target)
+ }
if !icu {
if mismatch := InterpolationMismatch(key, source, target); mismatch != nil {
findings = append(findings, protectedFinding(key, "interpolation variables", mismatch.SourceVars, mismatch.TargetVars))
}
}
checks := []struct {
- name string
- extract func(string) []string
+ name string
+ extractSource func(string) []string
+ extractTarget func(string) []string
}{
- {"HTML structure", extractHTMLTags},
- {"fenced code", extractFencedCode},
- {"inline code", extractInlineCode},
- {"markdown link destinations", extractLinkDestinations},
+ {"HTML structure", extractHTMLTags, extractHTMLTags},
+ {"fenced code", extractFencedCode, extractFencedCode},
+ {"inline code", extractInlineCode, extractInlineCode},
+ {"markdown link destinations", extractLinkDestinations, extractLinkDestinations},
+ }
+ if document {
+ checks[0].extractSource = func(input string) []string { return extractDocumentHTMLTags(input, sourcePath) }
+ checks[0].extractTarget = func(input string) []string { return extractDocumentHTMLTags(input, targetPath) }
+ checks[3].extractSource = func(input string) []string { return extractDocumentLinkDestinations(input, sourcePath) }
+ checks[3].extractTarget = func(input string) []string {
+ tokens := extractDocumentLinkDestinations(input, targetPath)
+ return removeSourceBacklink(tokens, sourcePath)
+ }
}
tokenizers := make([]func(string) []string, len(checks))
for index, check := range checks {
- tokenizers[index] = check.extract
+ tokenizers[index] = check.extractSource
}
preservedKinds := make([]bool, len(checks))
if icu {
@@ -46,8 +74,8 @@ func ProtectedFindings(key, source, target, targetLocale string) []Finding {
}
}
for index, check := range checks {
- sourceTokens := check.extract(source)
- targetTokens := check.extract(target)
+ sourceTokens := check.extractSource(source)
+ targetTokens := check.extractTarget(target)
preserved := equalStrings(sourceTokens, targetTokens)
if icu {
preserved = preservedKinds[index]
@@ -59,6 +87,62 @@ func ProtectedFindings(key, source, target, targetLocale string) []Finding {
return findings
}
+func extractDocumentHTMLTags(input, documentPath string) []string {
+ tags := extractHTMLTags(input)
+ for index, tag := range tags {
+ tags[index] = htmlPathAttrRe.ReplaceAllStringFunc(tag, func(attribute string) string {
+ match := htmlPathAttrRe.FindStringSubmatch(attribute)
+ if len(match) != 3 {
+ return attribute
+ }
+ value := strings.Trim(match[2], `"'`)
+ return strings.ToLower(match[1]) + `="` + resolveDocumentDestination(value, documentPath) + `"`
+ })
+ }
+ return tags
+}
+
+func extractDocumentLinkDestinations(input, documentPath string) []string {
+ destinations := extractLinkDestinations(input)
+ for index, destination := range destinations {
+ destinations[index] = resolveDocumentDestination(strings.Trim(destination, "<>"), documentPath)
+ }
+ return destinations
+}
+
+func removeSourceBacklink(destinations []string, sourcePath string) []string {
+ want, err := filepath.Abs(sourcePath)
+ if err != nil {
+ want = filepath.Clean(sourcePath)
+ }
+ want = filepath.ToSlash(want)
+ for index, destination := range destinations {
+ if destination == want {
+ return append(destinations[:index:index], destinations[index+1:]...)
+ }
+ }
+ return destinations
+}
+
+func resolveDocumentDestination(destination, documentPath string) string {
+ if destination == "" || strings.HasPrefix(destination, "#") || strings.HasPrefix(destination, "/") || uriSchemeRe.MatchString(destination) {
+ return destination
+ }
+ pathPart := destination
+ suffix := ""
+ if index := strings.IndexAny(pathPart, "?#"); index >= 0 {
+ pathPart, suffix = pathPart[:index], pathPart[index:]
+ }
+ if pathPart == "" {
+ return destination
+ }
+ base, err := filepath.Abs(filepath.Dir(documentPath))
+ if err != nil {
+ base = filepath.Clean(filepath.Dir(documentPath))
+ }
+ return filepath.ToSlash(filepath.Clean(filepath.Join(base, pathPart))) + suffix
+}
+
func extractHTMLTags(input string) []string {
return htmlTagRe.FindAllString(input, -1)
}
diff --git a/internal/validate/strict_test.go b/internal/validate/strict_test.go
index 6a344f1..7a61fc8 100644
--- a/internal/validate/strict_test.go
+++ b/internal/validate/strict_test.go
@@ -346,6 +346,38 @@ func TestProtectedFindingsUseStableCodeForEveryStructure(t *testing.T) {
}
}
+func TestProtectedDocumentFindingsResolveRelativeLinksAndAllowSourceBacklink(t *testing.T) {
+ source := `
[License](LICENSE) [Guide](docs/guide.md)`
+ target := `[English](../../README.md)
[Licence](../../LICENSE) [Guide](../guide.md)`
+ findings := ProtectedDocumentFindings(
+ "markdown:preamble",
+ source,
+ target,
+ "fr",
+ "/project/README.md",
+ "/project/docs/i18n/fr.md",
+ )
+ if len(findings) != 0 {
+ t.Fatalf("equivalent document links were rejected: %#v", findings)
+ }
+}
+
+func TestProtectedDocumentFindingsRejectDifferentResolvedLink(t *testing.T) {
+ source := `[Guide](docs/guide.md)`
+ target := `[Guide](../other.md)`
+ findings := ProtectedDocumentFindings(
+ "markdown:preamble",
+ source,
+ target,
+ "fr",
+ "/project/README.md",
+ "/project/docs/i18n/fr.md",
+ )
+ if len(findings) != 1 || findings[0].Code != CodeProtectedStructureMismatch {
+ t.Fatalf("different document link was accepted: %#v", findings)
+ }
+}
+
func TestProtectedFindingsCompareICUStructuresPerLocaleBranch(t *testing.T) {
tests := map[string]string{
"html": "Save",
diff --git a/internal/validate/validate.go b/internal/validate/validate.go
index 75d0b6f..471d002 100644
--- a/internal/validate/validate.go
+++ b/internal/validate/validate.go
@@ -113,7 +113,7 @@ func ValidateWithOptions(cfg *config.Config, opts Options) ([]Report, error) {
policyHash = resolved.Hash
}
- report := validateLocale(bundle.ID, cfg.SourceLocale, locale, sourceData, sourceKeys, targetPath, format, terms, manifest, policyHash, cfg.Validation.PluralStyle, opts)
+ report := validateLocale(bundle.ID, cfg.SourceLocale, locale, bundle.Source, sourceData, sourceKeys, targetPath, format, terms, manifest, policyHash, cfg.Validation.PluralStyle, opts)
reports = append(reports, report)
}
}
@@ -127,7 +127,7 @@ func formatForBundle(bundle config.Bundle) (formats.Format, error) {
return formats.FormatForFile(bundle.Source)
}
-func validateLocale(bundle, sourceLocale, locale string, sourceData []byte, sourceKeys map[string]string, targetPath string, format formats.Format, terms []glossary.Term, manifest *state.Manifest, policyHash, pluralStyle string, opts Options) Report {
+func validateLocale(bundle, sourceLocale, locale, sourcePath string, sourceData []byte, sourceKeys map[string]string, targetPath string, format formats.Format, terms []glossary.Term, manifest *state.Manifest, policyHash, pluralStyle string, opts Options) Report {
report := Report{Bundle: bundle, Locale: locale, TargetPath: targetPath}
if opts.Strict {
translated := 0.0
@@ -141,9 +141,13 @@ func validateLocale(bundle, sourceLocale, locale string, sourceData []byte, sour
validationKeys, requiredPluralKeys, optionalPluralKeys = ExpandI18nextV4Source(sourceKeys, sourceLocale, locale)
}
sourceICUValid := make(map[string]bool, len(validationKeys))
+ document := format.Name() == "markdown"
for _, key := range allKeys(validationKeys) {
sourceValue := validationKeys[key]
- findings := ICUSourceFindings(key, sourceValue, sourceLocale)
+ var findings []Finding
+ if !document {
+ findings = ICUSourceFindings(key, sourceValue, sourceLocale)
+ }
sourceICUValid[key] = len(findings) == 0
report.Findings = append(report.Findings, findings...)
}
@@ -209,11 +213,15 @@ func validateLocale(bundle, sourceLocale, locale string, sourceData []byte, sour
report.Mismatches = append(report.Mismatches, *mismatch)
}
}
- if sourceICUValid[key] {
+ if sourceICUValid[key] && !document {
report.Findings = append(report.Findings, ICUFindings(key, sourceValue, targetValue, locale)...)
}
if opts.Strict {
- report.Findings = append(report.Findings, ProtectedFindings(key, sourceValue, targetValue, locale)...)
+ if document {
+ report.Findings = append(report.Findings, ProtectedDocumentFindings(key, sourceValue, targetValue, locale, sourcePath, targetPath)...)
+ } else {
+ report.Findings = append(report.Findings, ProtectedFindings(key, sourceValue, targetValue, locale)...)
+ }
report.Findings = append(report.Findings, glossaryFindings(key, sourceValue, targetValue, terms)...)
}
if opts.RequireState {
diff --git a/scripts/generate-style-guides.mjs b/scripts/generate-style-guides.mjs
index f33778c..586cceb 100644
--- a/scripts/generate-style-guides.mjs
+++ b/scripts/generate-style-guides.mjs
@@ -7,6 +7,10 @@
* Usage:
* GOOGLE_AI_STUDIO_API_KEY=... node scripts/generate-style-guides.mjs
* GOOGLE_AI_STUDIO_API_KEY=... node scripts/generate-style-guides.mjs --locale ko
+ * GOOGLE_AI_STUDIO_API_KEY=... node scripts/generate-style-guides.mjs --locale ko --apply
+ *
+ * By default, output goes to .internationalizer/style-guide-candidates for
+ * review. --apply is required to replace tracked translation policy.
*/
import { writeFile, mkdir } from "node:fs/promises";
@@ -154,13 +158,16 @@ async function generateGuide(locale, info) {
},
};
- const url = `https://generativelanguage.googleapis.com/v1beta/models/${MODEL}:generateContent?key=${API_KEY}`;
+ const url = `https://generativelanguage.googleapis.com/v1beta/models/${MODEL}:generateContent`;
for (let attempt = 0; attempt < 3; attempt++) {
try {
const response = await fetch(url, {
method: "POST",
- headers: { "Content-Type": "application/json" },
+ headers: {
+ "Content-Type": "application/json",
+ "x-goog-api-key": API_KEY,
+ },
body: JSON.stringify(body),
});
@@ -200,13 +207,19 @@ async function generateGuide(locale, info) {
async function main() {
const args = process.argv.slice(2);
let filterLocale = null;
+ let apply = false;
for (let i = 0; i < args.length; i++) {
if (args[i] === "--locale" && args[i + 1]) {
filterLocale = args[i + 1];
}
+ if (args[i] === "--apply") {
+ apply = true;
+ }
}
- const outDir = join(process.cwd(), "style-guides");
+ const outDir = apply
+ ? join(process.cwd(), "style-guides")
+ : join(process.cwd(), ".internationalizer", "style-guide-candidates");
await mkdir(outDir, { recursive: true });
const localesToProcess = filterLocale
@@ -222,7 +235,8 @@ async function main() {
const entries = Object.entries(localesToProcess);
let completed = 0;
- console.error(`Generating style guides for ${entries.length} languages (concurrency: ${concurrency})...\n`);
+ const mode = apply ? "tracked guides" : "review candidates";
+ console.error(`Generating ${mode} for ${entries.length} languages (concurrency: ${concurrency})...\n`);
// Process in batches
for (let i = 0; i < entries.length; i += concurrency) {
@@ -245,6 +259,9 @@ async function main() {
}
console.error(`\nDone. Generated ${completed}/${entries.length} style guides in ${outDir}/`);
+ if (!apply) {
+ console.error("Review the candidates, then rerun with --apply to change tracked policy.");
+ }
}
main().catch((err) => {
diff --git a/style-guides/README.md b/style-guides/README.md
new file mode 100644
index 0000000..b410373
--- /dev/null
+++ b/style-guides/README.md
@@ -0,0 +1,39 @@
+# Translation style-guide policy
+
+Files in this directory are durable translation policy. Internationalizer reads
+them when it builds a locale prompt, but a translation run never edits them.
+Change a guide only when the language rule itself changes, not when application
+code or source copy changes.
+
+`_conventions.md` contains rules shared by every locale. Each `{locale}.md`
+file contains only language-specific grammar, register, typography, plural, and
+terminology decisions. Keep product facts in the source document instead of
+copying them into a language guide.
+
+Guide changes are intentionally visible: the manifest hashes each guide and
+marks that locale's existing translations as policy-stale. Internal prompt
+refactors do not have that effect unless the prompt-contract version changes.
+
+## Reference sources
+
+- Bangla, Czech, Danish, Dutch, Finnish, French, German, Greek, Hebrew, Hindi,
+ Indonesian, Italian, Korean, Malay, Polish, Portuguese (Brazil), Punjabi,
+ Romanian, Spanish, Swedish, Telugu, Thai, Turkish, Ukrainian, and Vietnamese:
+ [Microsoft Localization Style Guides](https://learn.microsoft.com/en-us/globalization/reference/microsoft-style-guides)
+- Japanese: JTF Japanese Standard Style Guide for Translation, plus the
+ supplied Japanese professional-writing reference
+- Russian: Microsoft Russian Style Guide and Nora Gal's writing guidance
+- Arabic: Microsoft Arabic (Saudi Arabia) Style Guide, Mahdi Alosh's usage
+ reference, and the supplied Arabic translation reference
+- Simplified and Traditional Chinese: the supplied Chinese editing and
+ translation references; regional terminology remains separate in each guide
+- Cantonese: the repository's Cantonese guide; Microsoft does not publish a
+ matching Cantonese guide in the collection above
+
+External references inform these concise project rules; they are not copied
+into the repository. Review model-proposed guide changes as policy changes,
+then update the guide deliberately and refresh affected translations.
+
+`scripts/generate-style-guides.mjs` writes review candidates under the ignored
+`.internationalizer/` directory by default. It changes tracked guides only
+when invoked with `--apply`.
From 0eb96d60343e169a3e20188665b7b7e2bcdf33f8 Mon Sep 17 00:00:00 2001
From: Tom Main
Date: Thu, 3 Sep 2026 22:22:46 -0400
Subject: [PATCH 4/4] docs: review all localized READMEs
---
docs/i18n/ar.md | 331 +++++++++++++++++++++++++++----------------
docs/i18n/bn.md | 319 +++++++++++++++++++++++++++---------------
docs/i18n/cs.md | 228 +++++++++++++++++++++---------
docs/i18n/da.md | 191 ++++++++++++++++++-------
docs/i18n/de.md | 306 +++++++++++++++++++++++++++-------------
docs/i18n/el.md | 205 ++++++++++++++++++++-------
docs/i18n/es.md | 325 ++++++++++++++++++++++++++++---------------
docs/i18n/fi.md | 211 +++++++++++++++++++++-------
docs/i18n/fr.md | 291 +++++++++++++++++++++++++-------------
docs/i18n/he.md | 242 ++++++++++++++++++++++----------
docs/i18n/hi.md | 308 +++++++++++++++++++++++++++-------------
docs/i18n/id.md | 309 +++++++++++++++++++++++++++--------------
docs/i18n/it.md | 211 +++++++++++++++++++++-------
docs/i18n/ja.md | 284 ++++++++++++++++++-------------------
docs/i18n/ko.md | 228 ++++++++++++++++++++++--------
docs/i18n/ms.md | 199 ++++++++++++++++++--------
docs/i18n/nl.md | 208 ++++++++++++++++++++-------
docs/i18n/pa.md | 248 ++++++++++++++++++++++++---------
docs/i18n/pl.md | 282 +++++++++++++++++++++++++------------
docs/i18n/pt-BR.md | 311 +++++++++++++++++++++++++++--------------
docs/i18n/ro.md | 213 ++++++++++++++++++++--------
docs/i18n/ru.md | 323 +++++++++++++++++++++++++++---------------
docs/i18n/sv.md | 189 ++++++++++++++++++-------
docs/i18n/te.md | 310 ++++++++++++++++++++++++++++-------------
docs/i18n/th.md | 339 +++++++++++++++++++++++++++++----------------
docs/i18n/tr.md | 198 ++++++++++++++++++++------
docs/i18n/uk.md | 330 ++++++++++++++++++++++++++++---------------
docs/i18n/vi.md | 199 +++++++++++++++++++-------
docs/i18n/yue.md | 302 +++++++++++++++++++++++++++-------------
docs/i18n/zh-CN.md | 320 +++++++++++++++++++++---------------------
docs/i18n/zh-TW.md | 281 +++++++++++++++++++------------------
31 files changed, 5559 insertions(+), 2682 deletions(-)
diff --git a/docs/i18n/ar.md b/docs/i18n/ar.md
index 0dbf6ee..90f6e3f 100644
--- a/docs/i18n/ar.md
+++ b/docs/i18n/ar.md
@@ -6,41 +6,47 @@
# Internationalizer
-مسار عمل أقلمة مدعوم بالذكاء الاصطناعي لمشاريع البرمجيات. ترجم ملفات i18n وتحقق من صحتها وأدرها باستخدام النماذج اللغوية الكبيرة (LLMs).
+مسار عمل تدويل أصيل بالذكاء الاصطناعي لمشاريع البرمجيات. ترجمة ملفات i18n والتحقق من صحتها وإدارتها باستخدام LLMs.
[](https://github.com/Tom-R-Main/Internationalizer/actions/workflows/ci.yml)
-[](LICENSE)
+[](../../LICENSE)
-## لماذا Internationalizer؟
+
+العربية · বাংলা · Čeština · Dansk · Deutsch · Ελληνικά · Español · Suomi · Français · עברית · हिन्दी · Indonesia · Italiano · 日本語 · 한국어 · Bahasa Melayu
Nederlands · ਪੰਜਾਬੀ · Polski · Português · Română · Русский · Svenska · తెలుగు · ไทย · Türkçe · Українська · Tiếng Việt · 粵語 · 简体中文 · 繁體中文
+
-معظم أدوات i18n هي إما مكتبات وقت التشغيل (مثل i18next، react-intl) أو منصات برمجيات كخدمة (SaaS) لإدارة المفاتيح (مثل Crowdin، Lokalise). لا تحل أي منها مشكلة الترجمة الفعلية بشكل جيد:
+---
+
+## لماذا Internationalizer؟
-- **الترجمة اليدوية** لا تتوسع بشكل جيد عند تجاوز بضع لغات
-- **واجهات برمجة تطبيقات الترجمة الآلية** (Google Translate، DeepL) تتجاهل مصطلحاتك ونبرتك وأعراف واجهة المستخدم الخاصة بك
-- **الترجمة العامة باستخدام LLM** تعمل بشكل أفضل، ولكن بدون مسارد وأدلة أسلوب، ستحصل على نتائج غير متسقة
+معظم أدوات i18n إما أن تكون مكتبات لوقت التشغيل (مثل i18next وreact-intl) أو منصات برمجيات كخدمة (SaaS) لإدارة المفاتيح (مثل Crowdin وLokalise). ولا تنجح أي منها في معالجة جوهر مشكلة الترجمة بكفاءة:
-أداة Internationalizer مختلفة. إنها **مسار عمل عبر واجهة سطر الأوامر (CLI)** يجمع بين ترجمة LLM مع:
+- **الترجمة اليدوية** يتعذر توسيع نطاقها عند تجاوز بضع لغات
+- **واجهات برمجة تطبيقات (API) الترجمة الآلية** (مثل Google Translate وDeepL) تتجاهل المصطلحات المعتمدة والنبرة وأعراف واجهة المستخدم في تطبيقك
+- **الترجمة العامة عبر LLM** تقدم أداءً أفضل، ولكن دون مسارد وأدلة أسلوب ستكون النتائج متباينة وغير متسقة
-- **مسارد لكل لغة** — لفرض مصطلحات متسقة عبر تطبيقك
-- **أدلة أسلوب لكل لغة** — للتحكم في النبرة، ومستوى الرسمية، وصيغ الجمع، والطباعة
-- **ذاكرة الترجمة** — لتخطي السلاسل النصية غير المتغيرة، وتوفير تكاليف استدعاءات واجهة برمجة التطبيقات (API)
-- **التحقق من صحة المفاتيح** — لاكتشاف الترجمات المفقودة وعدم تطابق المتغيرات (interpolation) قبل الإصدار
+تتميز أداة Internationalizer بنهج مختلف؛ إذ تمثل **مسار عمل عبر واجهة سطر الأوامر (CLI)** يدمج ترجمة LLM مع العناصر التالية:
+- **مسارد مخصصة لكل لغة** — لفرض مصطلحات متسقة في أرجاء التطبيق
+- **أدلة أسلوب مخصصة لكل لغة** — للتحكم في النبرة ومستوى الرسمية وصيغ الجمع وعلامات الترقيم
+- **ذاكرة ترجمة** — لتخطي السلاسل النصية غير المتغيرة وتوفير تكاليف استدعاءات API
+- **تحقق قطعي من الصحة** — لاكتشاف المفاتيح المفقودة أو الزائدة، والانحراف في البنى المحمية، ومخالفات المسرد، وأخطاء صيغ الجمع أو رسائل ICU قبل النشر الفعلي
+
## التثبيت
-التثبيت من npm:
+التثبيت عبر npm:
```bash
npm install -g internationalizer
```
-أو التشغيل بدون تثبيت عام:
+أو التشغيل المباشر دون تثبيت عام:
```bash
npx internationalizer --help
```
-تقوم حزمة npm بتثبيت الملف الثنائي (binary) المبني مسبقًا والمطابق من npm عبر التبعيات الاختيارية الخاصة بنظام التشغيل.
+تتولى حزمة npm تثبيت الملف الثنائي الجاهز والمطابق من npm عبر التبعيات الاختيارية الخاصة بكل منصة تشغيل.
التثبيت باستخدام Go:
@@ -48,30 +54,34 @@ npx internationalizer --help
go install github.com/Tom-R-Main/Internationalizer/cmd/internationalizer@latest
```
-أو البناء من المصدر:
+أو البناء من المصدر البرمجي:
```bash
git clone https://github.com/Tom-R-Main/Internationalizer.git
cd Internationalizer
go build -o internationalizer ./cmd/internationalizer
```
-
+
## حزم npm
-- يجب أن تتطابق علامات Git (tags) وإصدارات حزمة npm، على سبيل المثال `v0.1.0` و `0.1.0`
-- تعتمد حزمة `internationalizer` الجذرية على حزم أنظمة التشغيل مثل `internationalizer-darwin-arm64`
-- أهداف npm المدعومة: macOS arm64/x64، Linux arm64/x64، Windows x64
-- يتطلب النشر عبر CI وجود سر (secret) في GitHub باسم `NPM_TOKEN`
-
+- يجب تطابق وسوم Git وإصدارات حزمة npm، على سبيل المثال `v0.1.0` و`0.1.0`
+- تعتمد الحزمة الجذرية `internationalizer` على حزم المنصات المحددة مثل `internationalizer-darwin-arm64`
+- منصات npm المدعومة: macOS arm64/x64 وLinux arm64/x64 وWindows x64
+- يتطلب النشر عبر مسار CI وجود سر في GitHub باسم `NPM_TOKEN`
+
## البدء السريع
-1. أنشئ ملف تكوين في جذر مشروعك:
+1. إنشاء ملف تكوين في جذر المشروع:
```yaml
# .internationalizer.yml
source_locale: en
target_locales: [fr, de, es, ja]
-source_path: locales/en.json
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
llm:
provider: gemini
@@ -79,67 +89,95 @@ llm:
api_key_env: GOOGLE_AI_STUDIO_API_KEY
```
-2. قم بتعيين مفتاح API الخاص بك:
+2. ضبط مفتاح API في متغيرات البيئة:
```bash
export GOOGLE_AI_STUDIO_API_KEY=your-ai-studio-key
```
-3. عاين ما سيتم ترجمته:
+3. معاينة ما ستتم ترجمته:
```bash
internationalizer translate --dry-run
```
-4. قم بتشغيل الترجمة:
+4. تنفيذ الترجمة:
```bash
internationalizer translate
```
-5. تحقق من صحة جميع اللغات:
+5. التحقق من صحة جميع ملفات اللغات:
```bash
internationalizer validate
```
-
+
## الأوامر
### `translate`
-ابحث عن المفاتيح المفقودة وترجمها عبر LLM.
+استكشاف المفاتيح المفقودة أو القديمة وترجمتها عبر LLM.
```bash
-internationalizer translate # ترجمة جميع اللغات
-internationalizer translate -l fr # ترجمة الفرنسية فقط
-internationalizer translate --dry-run # معاينة بدون استدعاءات API
-internationalizer translate --batch-size 20 # دفعات أصغر
-internationalizer translate --concurrency 2 # استدعاءات متوازية أقل
+internationalizer translate # translate all locales
+internationalizer translate -l fr # translate French only
+internationalizer translate --dry-run # preview without API calls
+internationalizer translate --adopt-existing # baseline existing translations without API calls
+internationalizer translate --refresh-policy # refresh prompt/style/model-stale entries
+internationalizer translate --batch-size 20 # smaller batches
+internationalizer translate --concurrency 2 # fewer parallel calls
```
+تُبلغ حالة الترجمة بصورة مستقلة عن حالات الفقد، وقدم المصدر، وقدم السياسة، والحالة الراهنة، والتعديل اليدوي؛ ومن ثم لا يمكن لتعديل يدوي إخفاء أي تغيير يطرأ على المصدر أو السياسة. ويجري الإبلاغ عن القيم ذات السياسات القديمة دون إعادة ترجمتها تلقائيًا إلا بإضافة `--refresh-policy`. أما القيم المعدلة يدويًا فلا يتم استبدالها تلقائيًا على الإطلاق. يُستخدم الخيار `--adopt-existing` عند إدخال سجل التتبع إلى ترجمات تمت مراجعتها مسبقًا، أو عند اعتماد تعديل يدوي تمت مراجعته كخط أساس جديد صراحةً.
+
### `validate`
-تحقق من جميع ملفات اللغات بحثًا عن المفاتيح المفقودة، والمفاتيح الزائدة، وعدم تطابق المتغيرات (interpolation).
+فحص كافة ملفات اللغات ومقارنتها بحزم المصدر. يتحقق وضع الفحص الافتراضي من التغطية البنيوية (نسبة المفاتيح المطلوبة المتوفرة في ملفات الهدف)، ويُبلغ عن المفاتيح الزائدة كتحذيرات، بينما يفشل الفحص عند وجود مفاتيح مفقودة أو عدم تطابق في بنية المتغيرات (interpolation) أو وجود بنية غير صالحة في صياغة ICU MessageFormat.
```bash
-internationalizer validate # مخرجات قابلة للقراءة بشريًا
-internationalizer validate --json # مخرجات JSON قابلة للقراءة آليًا
-internationalizer validate -q # رمز الخروج (exit code) فقط
+internationalizer validate # human-readable output
+internationalizer validate --json # machine-readable JSON
+internationalizer validate -q # exit code only
+internationalizer validate --strict # enforce translation quality rules
+internationalizer validate --require-state # require current manifest provenance
```
+يُبلغ الخيار `--strict` أيضًا عن نسبة تغطية الترجمة. وتُعد القيمة اللغوية المطابقة لنصها المصدري غير مترجمة ما لم يتضمن المسرد صراحةً مدخلاً متطابق المصدر والهدف للقيمة بأكملها؛ مع مراعاة خيار `ignore_case`، علمًا بأن ورود مصطلح المسرد ضمن قيمة أطول لا يمنحها استثناءً. ويفشل الوضع الصارم عند وجود مفاتيح زائدة، أو قيم مطابقة للمصدر، أو حدوث تغيير في بنية المتغيرات أو HTML أو الأكواد أو روابط Markdown، أو انتهاك المسرد، أو غياب صيغ الجمع المحددة في الإعدادات.
+
+يتحقق الخيار `--require-state` من كل هدف مقابل ملف التتبع `.internationalizer.lock`. ويفشل الفحص إذا كان المفتاح غير متتبع، أو إذا كان تجزؤ المصدر أو سياسة الترجمة أو تجزؤ الهدف المسجل قديمًا. ويمكن دمجه مع الخيار `--strict`.
+
+تعتمد التقارير النصية وتقارير JSON رموز نتائج ثابتة:
+
+| الرمز | المعنى |
+| --- | --- |
+| `missing_key` / `extra_key` | اختلاف بين مجموعتي مفاتيح المصدر والهدف |
+| `blank_translation` | نص مصدري غير فارغ يقابله هدف فارغ في الوضع الصارم |
+| `source_identical` | بقاء قيمة لغوية غير مترجمة في الوضع الصارم |
+| `protected_structure_mismatch` | حدوث تغيير في بنية المتغيرات أو HTML أو التعليمات البرمجية أو الروابط |
+| `glossary_violation` | تعذر العثور على المصطلح المعتمد أو أحد متغيراته في لغة الهدف |
+| `plural_form_missing` | غياب إحدى صيغ الجمع المكونة للغة الهدف |
+| `icu_message_syntax` | بنية غير صحيحة لرسالة ICU في المصدر أو الهدف |
+| `icu_argument_mismatch` | اختلاف أسماء معاملات ICU أو أنواعها أو أنماط منسقاتها |
+| `icu_selector_mismatch` | اختلاف المحددات أو عدم صلاحية فئة الجمع للغة الهدف |
+| `untracked` | عدم وجود سجل في ملف التتبع للهدف |
+| `source_stale` | تغير محتوى المصدر بعد عملية الترجمة المسجلة |
+| `policy_stale` | تغير الموجه التوليدي (prompt) أو إعدادات النموذج |
+| `target_modified` | اختلاف محتوى الهدف عن السجل المحفوظ في ملف التتبع |
+
### `detect`
-اكتشاف إطار عمل i18n تلقائيًا واقتراح تكوين.
+اكتشاف إطار عمل i18n تلقائيًا واقتراح تكوين ملائم.
```bash
internationalizer detect
```
-يدعم: react-i18next، next-intl، vue-i18n، vanilla JSON، مستندات markdown.
+يدعم: react-i18next وnext-intl وvue-i18n وملفات JSON القياسية ووثائق markdown.
### `glossary`
-إدارة مصطلحات المسرد لكل لغة والتي يتم فرضها أثناء الترجمة.
+إدارة مصطلحات المسرد لكل لغة وفرضها أثناء الترجمة.
```bash
internationalizer glossary list --locale fr
@@ -149,166 +187,225 @@ internationalizer glossary remove --locale fr --source "Dashboard"
### `tm`
-إدارة ذاكرة الترجمة (ذاكرة تخزين مؤقت JSONL للسلاسل النصية المترجمة مسبقًا).
+إدارة ذاكرة الترجمة (ذاكرة تخزين مؤقت بصيغة JSONL للسلاسل النصية المترجمة سابقًا).
```bash
-internationalizer tm stats # عرض عدد السجلات
-internationalizer tm export # تصدير كـ JSON
-internationalizer tm clear --force # حذف جميع السجلات
+internationalizer tm stats # show record counts
+internationalizer tm export # dump as JSON
+internationalizer tm clear --force # delete all records
```
-
+
## مرجع التكوين
```yaml
# .internationalizer.yml
-# لغة المصدر (الافتراضي: en)
+# Source language (default: en)
source_locale: en
-# اللغات المراد الترجمة إليها (مطلوب)
-target_locales: [fr, de, es, ja, zh-CN, ar]
-
-# مسار ملف لغة المصدر (مطلوب)
-source_path: locales/en.json
-
-# إعدادات مزود LLM
+# Languages to translate into (required)
+target_locales: [fr, de, es, ja, yue, zh-CN, zh-TW, ar]
+
+# One or more source-to-target mappings (required).
+# {locale} is replaced with each configured target locale.
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
+ - id: docs
+ source: README.md
+ target: docs/i18n/{locale}.md
+ format: markdown
+
+# Backward compatibility: source_path still maps targets to sibling files
+# such as locales/fr.json. Prefer bundles for new projects.
+# source_path: locales/en.json
+
+# LLM provider settings
llm:
- # المزود: "anthropic" أو "openai" أو "gemini" أو "openrouter" (الافتراضي: gemini)
+ # Provider: "anthropic", "openai", "gemini", or "openrouter" (default: gemini)
provider: gemini
- # الأسماء الافتراضية للنماذج حسب المزود:
+ # Model name defaults by provider:
# anthropic: claude-opus-5
- # openai: gpt-5.6-luna
+ # openai: gpt-5.6-luna (reasoning effort defaults to max)
# gemini: gemini-3.8-flash
# openrouter: deepseek/deepseek-v4-pro-0813
model: gemini-3.8-flash
- # متغير البيئة الذي يحتوي على مفتاح API
+ # Environment variable containing the API key
api_key_env: GOOGLE_AI_STUDIO_API_KEY
- # عنوان URL الأساسي لنقاط النهاية المتوافقة مع OpenAI (اختياري)
+ # Base URL for OpenAI-compatible endpoints (optional)
# base_url: https://api.openai.com
-# عدد المفاتيح لكل استدعاء LLM (الافتراضي: 40)
+ # OpenAI GPT-5-series Responses API reasoning effort
+ # (default: max for the OpenAI provider)
+ reasoning_effort: max
+
+ # Optional LLM settings for individual target locales. An override using the
+ # global provider inherits unspecified global settings. A different provider
+ # uses that provider's defaults for unspecified settings.
+ locale_overrides:
+ yue:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-CN:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-TW:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+
+# Keys per LLM call (default: 40)
batch_size: 40
-# استدعاءات LLM المتوازية (الافتراضي: 4)
+# Parallel LLM calls (default: 4)
concurrency: 4
-# الدليل الذي يحتوي على ملفات Markdown لأدلة الأسلوب لكل لغة (الافتراضي: style-guides)
+# Directory containing per-locale style guide Markdown files (default: style-guides)
style_guides_dir: style-guides
-# الدليل الذي يحتوي على ملفات JSON للمسارد لكل لغة (الافتراضي: glossary)
+# Directory containing per-locale glossary JSON files (default: glossary)
glossary_dir: glossary
-# مسار ملف ذاكرة الترجمة (الافتراضي: .internationalizer/tm.jsonl)
+# Path to translation memory file (default: .internationalizer/tm.jsonl)
tm_path: .internationalizer/tm.jsonl
+
+# Versioned source, policy, target, and provenance state
+# (default: .internationalizer.lock; commit this file)
+manifest_path: .internationalizer.lock
+
+# Optional translation and strict-validation rules
+validation:
+ plural_style: i18next-v4 # generate and validate target-locale plural forms
```
+يجب أن تكون معرفات اللغات وسوم BCP 47 سليمة الصياغة مثل `fr` أو `pt-BR` أو `sr-Latn-RS`. وتُرفض اللغات الهدف المكافئة قياسيًا بصفتها تكرارًا، وتطابق التجاوزات المحددة للغات الهدف التهجئة المكافئة قياسيًا. وفي المثال الموضح أعلاه، ترث اللغات التي لم يُحدد لها تخصيص تجاوُزي — بما فيها اليابانية — تكوين Gemini العام.
+
+يجري تحليل قيم ICU MessageFormat بنيويًا. ويدعم النظام المعاملات البسيطة، و`select`، و`plural`، و`selectordinal`، و`number`، و`date`، و`time`، بما في ذلك الرسائل المتداخلة، وإزاحات الجمع (plural offsets)، ومحددات الأرقام الدقيقة، ورمز `#`. ويتحقق الفحص من الصحة النحوية، وأنواع المعاملات، وأنماط التنسيق، وإزاحات الجمع، وتطابق فروع الاختيار، وفئات جمع CLDR الخاصة باللغة الهدف. وتُرفض مخرجات المزود التي تخل بهذه الثوابت قبل حفظها في ملف اللغة أو في سجل ذاكرة الترجمة.
+
+عند تفعيل `i18next-v4`، يجري توسيع عائلات صيغ الجمع المكتشفة في لغة المصدر أثناء الترجمة إلى فئات CLDR الخاصة باللغة الهدف. وتستخدم الفئة المقتصرة على لغة الهدف قيمة `_other` في عائلة المصدر كقالب للترجمة. ويشترط التحقق الصارم وجود فئات الهدف تلك؛ في حين تُعد الفئات المقتصرة على لغة المصدر اختيارية للغات الهدف التي لا تستخدمها.
+
## أدلة الأسلوب
-أدلة الأسلوب هي ملفات Markdown يتم حقنها في موجه (prompt) ترجمة LLM. إنها تتحكم في النبرة، ومستوى الرسمية، والطباعة، والأعراف الأخرى الخاصة باللغة.
+أدلة الأسلوب هي ملفات Markdown تُحقن ضمن موجه (prompt) ترجمة LLM؛ للتحكم في النبرة، ومستوى الرسمية، وعلامات الترقيم، وسائر أعراف اللغة الخاصة.
```
style-guides/
- _conventions.md # قواعد مشتركة لجميع اللغات
- fr.md # قواعد خاصة باللغة الفرنسية
- ja.md # قواعد خاصة باللغة اليابانية
- ar.md # قواعد خاصة باللغة العربية
+ _conventions.md # shared rules for all languages
+ fr.md # French-specific rules
+ ja.md # Japanese-specific rules
+ ar.md # Arabic-specific rules
```
-### الأعراف المشتركة (`_conventions.md`)
+### الاتفاقيات المشتركة (`_conventions.md`)
-تحديد القواعد التي تنطبق على جميع اللغات: بناء جملة المتغيرات (interpolation)، والحفاظ على HTML، وأعراف أنواع السلاسل النصية (الأزرار مقابل التسميات مقابل الأخطاء)، إلخ.
+تحدد القواعد المطبقة على جميع اللغات: صياغة المتغيرات، والحفاظ على وسوم HTML، وأعراف أنواع السلاسل النصية (الأزرار مقابل التسميات مقابل رسائل الخطأ)، وما إلى ذلك.
-### أدلة لكل لغة (`{locale}.md`)
+### الأدلة المخصصة لكل لغة (`{locale}.md`)
-تحديد القواعد الخاصة باللغة: مستوى الرسمية (tu مقابل vous)، وعلامات الترقيم (علامات الاقتباس المزدوجة، وعلامات الاستفهام المقلوبة)، وصيغ الجمع، وتنسيق التواريخ/الأرقام، ومسرد المصطلحات.
+تحدد القواعد الخاصة بكل لغة: درجة الرسمية (مثل tu مقابل vous)، وعلامات الترقيم (كالأقواس المزدوجة وعلامات الاستفهام المعكوسة)، وصيغ الجمع، وتنسيق التواريخ والأرقام، ومسرد المصطلحات.
-راجع [`examples/react-app/style-guides/`](examples/react-app/style-guides/) للحصول على مثال عملي.
+تُعد أدلة الأسلوب مدخلات سياسة ثابتة وليست مخرجات مولدة؛ إذ تقرؤها أداة Internationalizer دون تعديل محتواها مطلقًا. ويجري تجزؤ محتواها بمعزل عن المسرد ومواصفات الموجه البرمجي، ومن ثم لا يؤدي تعديل شيفرة التطبيق إلى تقادم الترجمة. يؤدي تعديل الدليل عمدًا إلى تعليم تلك اللغة للمراجعة وفق السياسة؛ بينما لا يؤدي تعديل صياغة الموجه الداخلية إلى ذلك، ما لم يتغير إصدار مواصفات الموجه نفسه.
-## تنسيق المسرد
+راجع [`examples/react-app/style-guides/`](../../examples/react-app/style-guides/) للاطلاع على مثال عملي مكتمل.
+
+## صيغة المسرد
-ملفات المسرد عبارة عن مصفوفات JSON مخزنة في `{glossary_dir}/{locale}.json`:
+ملفات المسرد عبارة عن مصفوفات JSON مخزنة في المسار `{glossary_dir}/{locale}.json`:
```json
[
{
"source": "Dashboard",
"target": "Tableau de bord",
+ "variants": ["Panneau de contrôle"],
+ "enforcement": "error",
"ignore_case": false,
"whole_word": true
}
]
```
-يتم حقن المصطلحات في موجه LLM كجدول مصطلحات، مما يضمن ترجمة متسقة للمصطلحات الأساسية عبر تطبيقك.
-
+يحدد الحقل `variants` الصيغ المعتمدة الأخرى في لغة الهدف. ويأخذ الحقل `enforcement` إما القيمة `error` أو `warning`، أو يُترك فارغًا لاعتماد سلوك الخطأ الافتراضي. وتُحقن هذه المصطلحات في موجه LLM في هيئة جدول مصطلحات لضمان اتساق الترجمة عبر التطبيق بأكمله. كما يمنح وجود مدخل مطابق تمامًا مثل `{"source":"API","target":"API"}` استثناءً للقيمة المطابقة بالكامل لمصدرها من نتائج التحقق الصارم للقيم غير المترجمة؛ ولا يمنح هذا الاستثناء لقيمة أطول تشتمل على لفظ `API` فحسب.
+
## ذاكرة الترجمة
-يتم تخزين ذاكرة الترجمة كملف JSONL (سجل JSON واحد لكل سطر). يحتوي كل سجل على:
-
-- مفتاح وقيمة المصدر
-- القيمة المترجمة
-- تجزئة SHA-256 لقيمة المصدر
-- طابع زمني
+تُخزن ذاكرة الترجمة في ملف بتنسيق JSONL (سجل JSON واحد في كل سطر). ويتضمن كل سجل ما يلي:
-في عمليات التشغيل اللاحقة، يتم تقديم السلاسل النصية غير المتغيرة من ذاكرة التخزين المؤقت لـ TM دون استدعاء LLM، مما يوفر الوقت وتكاليف API. ملف TM متوافق مع git ويمكن إيداعه (commit) جنبًا إلى جنب مع ملفات اللغات الخاصة بك.
+- الحزمة، والمفتاح، وقيمة المصدر، والقيمة المترجمة، والاسم القياسي للغة الهدف
+- تجزؤ المصدر، ودليل الأسلوب، والمسرد، ومواصفات الموجه، وتجزؤ السياسة المجمعة
+- المزود والنموذج اللذين أنتجا الترجمة
+- طابعًا زمنيًا
+في عمليات التشغيل اللاحقة، تُسترجع السلاسل النصية ذات تجزؤ المصدر والسياسة المتطابقين مباشرة من التخزين المؤقت دون استدعاء LLM. ويكون المسار الافتراضي تحت المجلد المتجاهل `.internationalizer/` ليبقى تخزينًا مؤقتًا محليًا. ويمكن ضبط `tm_path` على مسار خاضع للتتبع إذا كان المشروع يتشارك ذاكرة الترجمة عن قصد. علمًا بأن ملف التتبع الخاضع للمراجعة `.internationalizer.lock` تتم إدارته بنسخ مستقلة.
+
## التنسيقات المدعومة
| التنسيق | الامتدادات | الوضع |
|--------|-----------|------|
-| JSON | `.json` | مفتاح-قيمة (متداخل، مسطح بترميز النقطة) |
+| JSON | `.json` | مفتاح-قيمة (متداخل، ومسطح بترميز النقطة) |
| YAML | `.yml`, `.yaml` | مفتاح-قيمة (يحافظ على التعليقات والترتيب) |
-| Markdown | `.md`, `.mdx` | ترجمة المستند بالكامل |
+| Markdown | `.md`, `.mdx` | المقدمة الاستهلالية والأقسام على مستوى عناوين H2 |
+تتضمن ملفات Markdown الهدف تعليقات `internationalizer:unit` غير مرئية قبل عناوين H2. وتتيح هذه العلامات الثابتة لأداة Internationalizer إضافة قسم واحد في المصدر أو نقله أو تعديله دون إعادة ترجمة الأقسام الأخرى التي لم تتغير. وستتلقى المستندات الحالية غير المميزة هذه العلامات عند أول تحديث ناجح لاحق.
+
## اكتشاف نوع المشروع
-يحدد `internationalizer detect` إعداد i18n الخاص بك عن طريق التحقق من:
+يتعرف أمر `internationalizer detect` على إعدادات i18n في مشروعك من خلال فحص ما يلي:
-- تبعيات `package.json` لـ react-i18next أو next-intl أو vue-i18n
-- هياكل الأدلة التي تطابق أنماط اللغات الشائعة
+- تبعيات `package.json` الخاصة بـ react-i18next أو next-intl أو vue-i18n
+- هياكل الأدلة المتطابقة مع أنماط اللغات الشائعة
- امتدادات الملفات وأعراف التسمية
-
-## البنية
+
+## البنية المعمارية
```
-cmd/internationalizer/ نقطة دخول CLI وتعريفات الأوامر
+cmd/internationalizer/ CLI entry point and command definitions
internal/
- config/ تحميل تكوين YAML مع الإعدادات الافتراضية
- detect/ اكتشاف نوع المشروع تلقائيًا
- formats/ محللات التنسيق (JSON، YAML، Markdown)
- glossary/ إدارة المسرد لكل لغة
- llm/ واجهة مزود LLM + التطبيقات
- anthropic.go الواجهة الخلفية لـ Anthropic Claude
- openai.go الواجهة الخلفية لـ OpenAI / المتوافقة
- gemini.go الواجهة الخلفية لـ Google Gemini عبر AI Studio
- يستخدم OpenRouter ملف openai.go مع base_url مخصص
- styleguide/ محمل دليل الأسلوب
- tm/ ذاكرة ترجمة JSONL
- translate/ منسق الترجمة
- validate/ التحقق من صحة اللغات ومقارنتها
+ config/ YAML config loading with defaults
+ detect/ Project type auto-detection
+ formats/ Format parsers (JSON, YAML, Markdown)
+ glossary/ Per-locale glossary management
+ llm/ LLM provider interface + implementations
+ anthropic.go Anthropic Claude backend
+ openai.go OpenAI / compatible backend
+ gemini.go Google Gemini via AI Studio backend
+ OpenRouter uses openai.go with custom base_url
+ locale/ BCP 47 identity and CLDR plural categories
+ message/ ICU MessageFormat parser and structural comparison
+ policy/ Stable translation-policy hashing
+ state/ Versioned translation manifest
+ styleguide/ Style guide loader
+ tm/ JSONL translation memory
+ translate/ Translation orchestrator
+ validate/ Locale validation and diffing
```
-
+
## مقارنة مع البدائل
| الميزة | Internationalizer | i18next | Crowdin | LLM عام |
|---------|------------------|---------|---------|-------------|
| ترجمة مدعومة بـ LLM | نعم | لا | جزئي | نعم |
-| أدلة أسلوب لكل لغة | نعم | لا | لا | لا |
+| أدلة أسلوب مخصصة لكل لغة | نعم | لا | لا | لا |
| فرض المسرد | نعم | لا | نعم | لا |
| ذاكرة الترجمة | نعم | لا | نعم | لا |
-| CLI / تنفيذ محلي | نعم | غير متوفر | لا | يدوي |
-| ملفات متوافقة مع Git | نعم | نعم | جزئي | يدوي |
-| بدون الاعتماد على SaaS | نعم | نعم | لا | يختلف |
-| مفتوح المصدر (AGPL-3.0) | نعم | نعم | لا | يختلف |
-
+| واجهة سطر أوامر / تنفيذ محلي | نعم | لا ينطبق | لا | يدوي |
+| ملفات ملائمة لنظام Git | نعم | نعم | جزئي | يدوي |
+| خلو من الاعتماد على SaaS | نعم | نعم | لا | يتفاوت |
+| مصدر مفتوح (AGPL-3.0) | نعم | نعم | لا | يتفاوت |
+
## الترخيص
-[AGPL-3.0](LICENSE)
+[AGPL-3.0](../../LICENSE)
+راجع [THIRD_PARTY_NOTICES.md](../../THIRD_PARTY_NOTICES.md) للاطلاع على إشعارات تبعيات الطرف الثالث.
+
## المساهمة
-راجع [CONTRIBUTING.md](CONTRIBUTING.md) لإعداد التطوير والإرشادات. تتطلب جميع المساهمات توقيع DCO.
-
+راجع [CONTRIBUTING.md](../../CONTRIBUTING.md) لمعرفة إرشادات الإعداد والمساهمة في التطوير. تتطلب كافة المساهمات التوقيع وفق شهادة أصل المطور (DCO).
diff --git a/docs/i18n/bn.md b/docs/i18n/bn.md
index 5869c67..f95b291 100644
--- a/docs/i18n/bn.md
+++ b/docs/i18n/bn.md
@@ -6,26 +6,32 @@
# Internationalizer
-সফটওয়্যার প্রজেক্টের জন্য এআই-নেটিভ (AI-native) ইন্টারন্যাশনালাইজেশন পাইপলাইন। LLM ব্যবহার করে i18n ফাইলগুলো অনুবাদ, যাচাই এবং পরিচালনা করুন।
+সফটওয়্যার প্রজেক্টের জন্য AI-নেটিভ আন্তর্জাতিকীকরণ (internationalization) পাইপলাইন। LLM ব্যবহার করে i18n ফাইল অনুবাদ করুন, যাচাই (validate) করুন এবং পরিচালনা করুন।
[](https://github.com/Tom-R-Main/Internationalizer/actions/workflows/ci.yml)
-[](LICENSE)
+[](../../LICENSE)
-## Internationalizer কেন?
+
+العربية · বাংলা · Čeština · Dansk · Deutsch · Ελληνικά · Español · Suomi · Français · עברית · हिन्दी · Indonesia · Italiano · 日本語 · 한국어 · Bahasa Melayu
Nederlands · ਪੰਜਾਬੀ · Polski · Português · Română · Русский · Svenska · తెలుగు · ไทย · Türkçe · Українська · Tiếng Việt · 粵語 · 简体中文 · 繁體中文
+
-বেশিরভাগ i18n টুল হয় রানটাইম লাইব্রেরি (i18next, react-intl) অথবা কি-ম্যানেজমেন্ট SaaS প্ল্যাটফর্ম (Crowdin, Lokalise)। এগুলোর কোনোটিই অনুবাদের মূল সমস্যাটি ভালোভাবে সমাধান করতে পারে না:
+---
+
+## Internationalizer কেন?
-- **ম্যানুয়াল অনুবাদ** কয়েকটি ভাষার বেশি স্কেল করা যায় না
-- **মেশিন ট্রান্সলেশন API** (Google Translate, DeepL) আপনার পরিভাষা, টোন এবং UI কনভেনশনগুলোকে উপেক্ষা করে
-- **সাধারণ LLM অনুবাদ** তুলনামূলক ভালো কাজ করে, কিন্তু গ্লোসারি এবং স্টাইল গাইড ছাড়া আপনি অসামঞ্জস্যপূর্ণ ফলাফল পাবেন
+অধিকাংশ i18n টুল হয় রানটাইম লাইব্রেরি (i18next, react-intl) নয়তো কি-ম্যানেজমেন্ট SaaS প্ল্যাটফর্ম (Crowdin, Lokalise)। এগুলোর কোনোটিই অনুবাদের আসল সমস্যার কার্যকর সমাধান করে না:
-Internationalizer আলাদা। এটি একটি **CLI পাইপলাইন** যা LLM অনুবাদের সাথে নিচের বিষয়গুলোকে যুক্ত করে:
+- **ম্যানুয়াল অনুবাদ** কয়েকটি ভাষার বেশি স্কেল করতে পারে না
+- **মেশিন ট্রান্সলেশন API** (Google Translate, DeepL) আপনার পরিভাষা, টোন এবং UI কনভেনশন উপেক্ষা করে
+- **সাধারণ LLM অনুবাদ** তুলনামূলক ভালো হলেও গ্লোসারি এবং স্টাইল গাইড ছাড়া অসামঞ্জস্যপূর্ণ ফলাফল দেয়
-- **ভাষাভিত্তিক গ্লোসারি** — আপনার অ্যাপজুড়ে সামঞ্জস্যপূর্ণ পরিভাষা নিশ্চিত করে
-- **ভাষাভিত্তিক স্টাইল গাইড** — টোন, ফর্মালিটি, বহুবচন এবং টাইপোগ্রাফি নিয়ন্ত্রণ করে
-- **ট্রান্সলেশন মেমরি** — অপরিবর্তিত স্ট্রিংগুলো এড়িয়ে যায়, API কলের খরচ বাঁচায়
-- **কি (Key) ভ্যালিডেশন** — রিলিজের আগেই বাদ পড়া অনুবাদ এবং ইন্টারপোলেশনের অমিলগুলো ধরে ফেলে
+Internationalizer সম্পূর্ণ আলাদা। এটি একটি **CLI পাইপলাইন**, যা LLM অনুবাদের সাথে নিচের সুবিধাগুলো যুক্ত করে:
+- **ভাষাভিত্তিক গ্লোসারি** — আপনার পুরো অ্যাপ্লিকেশনে সুসংগত পরিভাষা বজায় রাখে
+- **ভাষাভিত্তিক স্টাইল গাইড** — টোন, ফর্মালিটি, বহুবচন রূপ এবং টাইপোগ্রাফি নিয়ন্ত্রণ করে
+- **ট্রান্সলেশন মেমরি** — অপরিবর্তিত স্ট্রিংগুলো এড়িয়ে গিয়ে API কলের খরচ বাঁচায়
+- **ডিটারমিনিস্টিক ভ্যালিডেশন** — বাদ পড়া বা অতিরিক্ত কি, সুরক্ষিত কাঠামোর বিচ্যুতি, গ্লোসারি সংক্রান্ত অসঙ্গতি এবং বহুবচন বা ICU ত্রুটি রিলিজের আগেই শনাক্ত করে
+
## ইনস্টলেশন
npm থেকে ইনস্টল করুন:
@@ -34,15 +40,15 @@ npm থেকে ইনস্টল করুন:
npm install -g internationalizer
```
-অথবা গ্লোবাল ইনস্টল ছাড়াই রান করুন:
+অথবা গ্লোবাল ইনস্টলেশন ছাড়াই রান করুন:
```bash
npx internationalizer --help
```
-npm প্যাকেজটি প্ল্যাটফর্ম-নির্দিষ্ট অপশনাল ডিপেন্ডেন্সির মাধ্যমে npm থেকে মানানসই প্রি-বিল্ট বাইনারি ইনস্টল করে।
+npm প্যাকেজটি প্ল্যাটফর্ম-নির্দিষ্ট অপশনাল ডিপেন্ডেন্সির মাধ্যমে npm থেকে উপযুক্ত প্রি-বিল্ট বাইনারি ইনস্টল করে।
-Go দিয়ে ইনস্টল করুন:
+Go ব্যবহার করে ইনস্টল করুন:
```bash
go install github.com/Tom-R-Main/Internationalizer/cmd/internationalizer@latest
@@ -55,23 +61,27 @@ git clone https://github.com/Tom-R-Main/Internationalizer.git
cd Internationalizer
go build -o internationalizer ./cmd/internationalizer
```
-
+
## npm প্যাকেজসমূহ
-- Git ট্যাগ এবং npm প্যাকেজ ভার্সন অবশ্যই মিলতে হবে, উদাহরণস্বরূপ `v0.1.0` এবং `0.1.0`
-- রুট `internationalizer` প্যাকেজটি প্ল্যাটফর্ম প্যাকেজগুলোর ওপর নির্ভর করে, যেমন `internationalizer-darwin-arm64`
-- সাপোর্টেড npm টার্গেট: macOS arm64/x64, Linux arm64/x64, Windows x64
-- CI পাবলিশিংয়ের জন্য `NPM_TOKEN` নামের একটি GitHub সিক্রেট প্রয়োজন
-
+- Git ট্যাগ এবং npm প্যাকেজের সংস্করণ অবশ্যই এক হতে হবে, যেমন `v0.1.0` এবং `0.1.0`
+- রুট `internationalizer` প্যাকেজটি প্ল্যাটফর্ম প্যাকেজের ওপর নির্ভরশীল, যেমন `internationalizer-darwin-arm64`
+- সমর্থিত npm প্ল্যাটফর্ম: macOS arm64/x64, Linux arm64/x64, Windows x64
+- CI প্রকাশের জন্য `NPM_TOKEN` নামের একটি GitHub সিক্রেট প্রয়োজন
+
## কুইক স্টার্ট
-১. আপনার প্রজেক্ট রুটে একটি কনফিগ ফাইল তৈরি করুন:
+১. আপনার প্রজেক্টের রুটে একটি কনফিগারেশন ফাইল তৈরি করুন:
```yaml
# .internationalizer.yml
source_locale: en
target_locales: [fr, de, es, ja]
-source_path: locales/en.json
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
llm:
provider: gemini
@@ -79,67 +89,95 @@ llm:
api_key_env: GOOGLE_AI_STUDIO_API_KEY
```
-২. আপনার API কি (key) সেট করুন:
+২. আপনার API কী সেট করুন:
```bash
export GOOGLE_AI_STUDIO_API_KEY=your-ai-studio-key
```
-৩. কী অনুবাদ করা হবে তার প্রিভিউ দেখুন:
+৩. কী অনুবাদ হতে চলেছে তার প্রিভিউ দেখুন:
```bash
internationalizer translate --dry-run
```
-৪. অনুবাদ শুরু করুন:
+৪. অনুবাদ প্রক্রিয়াটি রান করুন:
```bash
internationalizer translate
```
-৫. সব লোকেল (locales) ভ্যালিডেট করুন:
+৫. সমস্ত লোকেল ভ্যালিডেট করুন:
```bash
internationalizer validate
```
-
+
## কমান্ডসমূহ
### `translate`
-বাদ পড়া কি (keys) খুঁজে বের করুন এবং একটি LLM-এর মাধ্যমে সেগুলো অনুবাদ করুন।
+অনুপস্থিত বা পুরানো কি (stale keys) খুঁজে বের করুন এবং LLM-এর সাহায্যে অনুবাদ করুন।
```bash
-internationalizer translate # সব লোকেল অনুবাদ করুন
-internationalizer translate -l fr # শুধু ফ্রেঞ্চ অনুবাদ করুন
-internationalizer translate --dry-run # API কল ছাড়াই প্রিভিউ দেখুন
-internationalizer translate --batch-size 20 # ছোট ব্যাচ
-internationalizer translate --concurrency 2 # কম প্যারালাল কল
+internationalizer translate # translate all locales
+internationalizer translate -l fr # translate French only
+internationalizer translate --dry-run # preview without API calls
+internationalizer translate --adopt-existing # baseline existing translations without API calls
+internationalizer translate --refresh-policy # refresh prompt/style/model-stale entries
+internationalizer translate --batch-size 20 # smaller batches
+internationalizer translate --concurrency 2 # fewer parallel calls
```
+ট্রান্সলেশন স্টেট স্বতন্ত্রভাবে অনুপস্থিত (missing), সোর্স পরিবর্তনের কারণে পুরানো (source-stale), পলিসি পরিবর্তনের কারণে পুরানো (policy-stale), বর্তমান (current) এবং ম্যানুয়ালি এডিট করা (manually edited) অবস্থা রিপোর্ট করে; ফলে কোনো ম্যানুয়াল এডিট কখনোই সোর্স বা পলিসির পরিবর্তন আড়াল করতে পারে না। পলিসি পরিবর্তনের কারণে পুরানো মানগুলো চিহ্নিত করা হয়, তবে শুধুমাত্র `--refresh-policy` ফ্ল্যাগ দিলে পুনরায় অনুবাদ করা হয়। ম্যানুয়ালি এডিট করা মান কখনোই স্বয়ংক্রিয়ভাবে ওভাররাইট হয় না। পূর্বে পর্যালোচিত কোনো অনুবাদকে ম্যানিফেস্টে যুক্ত করতে অথবা পর্যালোচিত ম্যানুয়াল এডিটকে নতুন বেসলাইন হিসেবে সুস্পষ্টভাবে গ্রহণ করতে `--adopt-existing` ব্যবহার করুন।
+
### `validate`
-বাদ পড়া কি, অতিরিক্ত কি এবং ইন্টারপোলেশনের অমিল খুঁজতে সব লোকেল ফাইল চেক করুন।
+সোর্স বান্ডেলের বিপরীতে সমস্ত লোকেল ফাইল যাচাই করুন। ডিফল্ট ভ্যালিডেশন কাঠামোগত কভারেজ (প্রয়োজনীয় টার্গেট কি-র উপস্থিতির শতাংশ) পরীক্ষা করে, অতিরিক্ত কি-গুলোকে ওয়ার্নিং হিসেবে রিপোর্ট করে এবং কোনো কি অনুপস্থিত থাকলে, ইন্টারপোলেশন অমিল হলে বা অবৈধ ICU MessageFormat কাঠামো থাকলে ব্যর্থ (fail) হয়।
```bash
-internationalizer validate # মানুষের পড়ার যোগ্য আউটপুট
-internationalizer validate --json # মেশিন-রিডেবল JSON
-internationalizer validate -q # শুধু এক্সিট কোড
+internationalizer validate # human-readable output
+internationalizer validate --json # machine-readable JSON
+internationalizer validate -q # exit code only
+internationalizer validate --strict # enforce translation quality rules
+internationalizer validate --require-state # require current manifest provenance
```
+`--strict` ফ্ল্যাগ অনুবাদের কভারেজও রিপোর্ট করে। সোর্সের সাথে হুবহু অভিন্ন কোনো ভাষাগত মানকে অনুদিত ধরা হয় না, যদি না গ্লোসারিতে সম্পূর্ণ মানটির জন্য একই সোর্স ও একই টার্গেট সংবলিত সুস্পষ্ট এন্ট্রি থাকে; এতে `ignore_case` মান্য করা হয়, তবে কোনো দীর্ঘ মানের অংশ হিসেবে উপস্থিত গ্লোসারি টার্ম এর আওতাভুক্ত নয়। স্ট্রিক্ট মোড অতিরিক্ত কি, সোর্স-অভিন্ন মান, পরিবর্তিত ইন্টারপোলেশন/HTML/কোড/Markdown লিংকের কাঠামো, গ্লোসারি লঙ্ঘন এবং কনফিগার করা বহুবচন রূপের অনুপস্থিতির ক্ষেত্রে ব্যর্থ হয়।
+
+`--require-state` প্রতিটি টার্গেটকে `.internationalizer.lock`-এর বিপরীতে যাচাই করে। কোনো কি ট্র্যাক না করা থাকলে, অথবা নথিভুক্ত সোর্স, ট্রান্সলেশন পলিসি বা টার্গেট হ্যাশ পুরানো হলে এটি ব্যর্থ হয়। এটি `--strict`-এর সাথেও ব্যবহার করা যায়।
+
+হিউম্যান এবং JSON রিপোর্টে নিচের অপরিবর্তনশীল ফাইন্ডিং কোডগুলো ব্যবহৃত হয়:
+
+| কোড | অর্থ |
+| --- | --- |
+| `missing_key` / `extra_key` | সোর্স এবং টার্গেটের কি সেটের মধ্যে অমিল রয়েছে |
+| `blank_translation` | একটি অ-শূন্য সোর্সের জন্য স্ট্রিক্ট মোডে টার্গেট শূন্য পাওয়া গেছে |
+| `source_identical` | স্ট্রিক্ট মোডে একটি ভাষাগত মান অনুবাদহীন রয়ে গেছে |
+| `protected_structure_mismatch` | ইন্টারপোলেশন, HTML, কোড বা লিংকের কাঠামো পরিবর্তিত হয়েছে |
+| `glossary_violation` | কোনো অনুমোদিত টার্গেট টার্ম বা ভ্যারিয়েন্ট পাওয়া যায়নি |
+| `plural_form_missing` | লোকেলের জন্য কনফিগার করা বহুবচন রূপ অনুপস্থিত |
+| `icu_message_syntax` | সোর্স বা টার্গেটের ICU মেসেজের গঠন ত্রুটিপূর্ণ |
+| `icu_argument_mismatch` | ICU আর্গুমেন্টের নাম, ধরন বা ফরম্যাটার স্টাইল আলাদা |
+| `icu_selector_mismatch` | সিলেক্টরে অমিল রয়েছে অথবা টার্গেট লোকেলের জন্য প্লুরাল ক্যাটাগরি অবৈধ |
+| `untracked` | টার্গেটের জন্য ম্যানিফেস্টে কোনো রেকর্ড নেই |
+| `source_stale` | নথিভুক্ত অনুবাদের পর সোর্সের বিষয়বস্তু পরিবর্তিত হয়েছে |
+| `policy_stale` | জেনারেট করা প্রম্পট বা মডেল সেটিংস পরিবর্তিত হয়েছে |
+| `target_modified` | টার্গেটের বিষয়বস্তু ম্যানিফেস্ট রেকর্ডের সাথে মিলছে না |
+
### `detect`
-i18n ফ্রেমওয়ার্ক স্বয়ংক্রিয়ভাবে শনাক্ত করুন এবং একটি কনফিগারেশনের পরামর্শ দিন।
+i18n ফ্রেমওয়ার্ক স্বয়ংক্রিয়ভাবে শনাক্ত করুন এবং উপযুক্ত কনফিগারেশনের প্রস্তাব পান।
```bash
internationalizer detect
```
-সাপোর্ট করে: react-i18next, next-intl, vue-i18n, ভ্যানিলা JSON, মার্কডাউন ডক্স।
+সমর্থিত ফ্রেমওয়ার্ক: react-i18next, next-intl, vue-i18n, ভ্যানিলা JSON, Markdown ডক্স।
### `glossary`
-ভাষাভিত্তিক গ্লোসারি টার্মগুলো পরিচালনা করুন যা অনুবাদের সময় প্রয়োগ করা হয়।
+ভাষাভিত্তিক গ্লোসারি টার্ম পরিচালনা করুন, যা অনুবাদের সময় কঠোরভাবে প্রয়োগ করা হয়।
```bash
internationalizer glossary list --locale fr
@@ -149,148 +187,206 @@ internationalizer glossary remove --locale fr --source "Dashboard"
### `tm`
-ট্রান্সলেশন মেমরি পরিচালনা করুন (আগে অনুবাদ করা স্ট্রিংগুলোর JSONL ক্যাশ)।
+ট্রান্সলেশন মেমরি (পূর্বে অনুবাদ করা স্ট্রিংয়ের JSONL ক্যাশ) পরিচালনা করুন।
```bash
-internationalizer tm stats # রেকর্ডের সংখ্যা দেখান
-internationalizer tm export # JSON হিসেবে ডাম্প করুন
-internationalizer tm clear --force # সব রেকর্ড মুছে ফেলুন
+internationalizer tm stats # show record counts
+internationalizer tm export # dump as JSON
+internationalizer tm clear --force # delete all records
```
-
+
## কনফিগারেশন রেফারেন্স
```yaml
# .internationalizer.yml
-# সোর্স ভাষা (ডিফল্ট: en)
+# Source language (default: en)
source_locale: en
-# যেসব ভাষায় অনুবাদ করতে হবে (আবশ্যক)
-target_locales: [fr, de, es, ja, zh-CN, ar]
-
-# সোর্স লোকেল ফাইলের পাথ (আবশ্যক)
-source_path: locales/en.json
-
-# LLM প্রোভাইডার সেটিংস
+# Languages to translate into (required)
+target_locales: [fr, de, es, ja, yue, zh-CN, zh-TW, ar]
+
+# One or more source-to-target mappings (required).
+# {locale} is replaced with each configured target locale.
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
+ - id: docs
+ source: README.md
+ target: docs/i18n/{locale}.md
+ format: markdown
+
+# Backward compatibility: source_path still maps targets to sibling files
+# such as locales/fr.json. Prefer bundles for new projects.
+# source_path: locales/en.json
+
+# LLM provider settings
llm:
- # প্রোভাইডার: "anthropic", "openai", "gemini", অথবা "openrouter" (ডিফল্ট: gemini)
+ # Provider: "anthropic", "openai", "gemini", or "openrouter" (default: gemini)
provider: gemini
- # প্রোভাইডার অনুযায়ী ডিফল্ট মডেলের নাম:
+ # Model name defaults by provider:
# anthropic: claude-opus-5
- # openai: gpt-5.6-luna
+ # openai: gpt-5.6-luna (reasoning effort defaults to max)
# gemini: gemini-3.8-flash
# openrouter: deepseek/deepseek-v4-pro-0813
model: gemini-3.8-flash
- # API কি ধারণকারী এনভায়রনমেন্ট ভেরিয়েবল
+ # Environment variable containing the API key
api_key_env: GOOGLE_AI_STUDIO_API_KEY
- # OpenAI-সামঞ্জস্যপূর্ণ এন্ডপয়েন্টগুলোর জন্য বেস URL (ঐচ্ছিক)
+ # Base URL for OpenAI-compatible endpoints (optional)
# base_url: https://api.openai.com
-# প্রতি LLM কলে কি-এর সংখ্যা (ডিফল্ট: 40)
+ # OpenAI GPT-5-series Responses API reasoning effort
+ # (default: max for the OpenAI provider)
+ reasoning_effort: max
+
+ # Optional LLM settings for individual target locales. An override using the
+ # global provider inherits unspecified global settings. A different provider
+ # uses that provider's defaults for unspecified settings.
+ locale_overrides:
+ yue:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-CN:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-TW:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+
+# Keys per LLM call (default: 40)
batch_size: 40
-# প্যারালাল LLM কল (ডিফল্ট: 4)
+# Parallel LLM calls (default: 4)
concurrency: 4
-# ভাষাভিত্তিক স্টাইল গাইড মার্কডাউন ফাইল ধারণকারী ডিরেক্টরি (ডিফল্ট: style-guides)
+# Directory containing per-locale style guide Markdown files (default: style-guides)
style_guides_dir: style-guides
-# ভাষাভিত্তিক গ্লোসারি JSON ফাইল ধারণকারী ডিরেক্টরি (ডিফল্ট: glossary)
+# Directory containing per-locale glossary JSON files (default: glossary)
glossary_dir: glossary
-# ট্রান্সলেশন মেমরি ফাইলের পাথ (ডিফল্ট: .internationalizer/tm.jsonl)
+# Path to translation memory file (default: .internationalizer/tm.jsonl)
tm_path: .internationalizer/tm.jsonl
+
+# Versioned source, policy, target, and provenance state
+# (default: .internationalizer.lock; commit this file)
+manifest_path: .internationalizer.lock
+
+# Optional translation and strict-validation rules
+validation:
+ plural_style: i18next-v4 # generate and validate target-locale plural forms
```
+লোকেল শনাক্তকারীগুলো অবশ্যই BCP 47 ফরম্যাট মেনে গঠিত হতে হবে, যেমন `fr`, `pt-BR`, বা `sr-Latn-RS`। ক্যানোনিকাল-সমতুল্য টার্গেট লোকেলগুলো ডুপ্লিকেট হিসেবে বাতিল করা হয় এবং লোকেল-নির্দিষ্ট প্রোভাইডার ওভাররাইড ক্যানোনিকাল-সমতুল্য বানানের সাথে মেলানো হয়। উপরের উদাহরণে, ওভাররাইড ছাড়া অন্যান্য লোকেল—যেমন জাপানি ভাষা—স্বয়ংক্রিয়ভাবে গ্লোবাল Gemini কনফিগারেশন গ্রহণ করে।
+
+ICU MessageFormat মানগুলো কাঠামোগতভাবে পার্স করা হয়। সাধারণ আর্গুমেন্ট, `select`, `plural`, `selectordinal`, `number`, `date`, এবং `time` সমর্থিত, যার মধ্যে নেস্টেড মেসেজ, প্লুরাল অফসেট, নির্দিষ্ট সংখ্যার সিলেক্টর এবং `#` অন্তর্ভুক্ত। ভ্যালিডেশন প্রক্রিয়ায় সিনট্যাক্স, আর্গুমেন্টের ধরন ও ফরম্যাটার স্টাইল, প্লুরাল অফসেট, সিলেক্ট ব্রাঞ্চের সমতা এবং টার্গেট লোকেলের CLDR প্লুরাল ক্যাটাগরি পরীক্ষা করা হয়। প্রোভাইডারের আউটপুট যদি এই নিয়মগুলো ভঙ্গ করে, তবে লোকেল ফাইল বা ট্রান্সলেশন মেমরিতে রেকর্ড লেখার আগেই তা বাতিল করা হয়।
+
+`i18next-v4`-এর ক্ষেত্রে, অনুবাদের সময় স্বীকৃত সোর্স প্লুরাল ফ্যামিলিগুলোকে টার্গেট লোকেলের CLDR ক্যাটাগরি অনুযায়ী প্রসারিত করা হয়। শুধুমাত্র টার্গেটে থাকা কোনো ক্যাটাগরির ক্ষেত্রে সোর্স ফ্যামিলির `_other` মানকে অনুবাদের টেমপ্লেট হিসেবে ব্যবহার করা হয়। স্ট্রিক্ট ভ্যালিডেশনে এই টার্গেট ক্যাটাগরিগুলো থাকা বাধ্যতামূলক; সোর্সে থাকা কিন্তু টার্গেট লোকেলে অপ্রয়োজনীয় ক্যাটাগরিগুলো ঐচ্ছিক।
+
## স্টাইল গাইড
-স্টাইল গাইড হলো মার্কডাউন ফাইল যা LLM ট্রান্সলেশন প্রম্পটে ইনজেক্ট করা হয়। এগুলো টোন, ফর্মালিটি, টাইপোগ্রাফি এবং অন্যান্য ভাষাগত কনভেনশন নিয়ন্ত্রণ করে।
+স্টাইল গাইড হলো Markdown ফাইল, যা অনুবাদের সময় LLM প্রম্পটের মধ্যে যুক্ত করা হয়। এগুলো টোন, ফর্মালিটি, টাইপোগ্রাফি এবং ভাষা-নির্দিষ্ট অন্যান্য রীতিনীতি নিয়ন্ত্রণ করে।
```
style-guides/
- _conventions.md # সব ভাষার জন্য সাধারণ নিয়ম
- fr.md # ফ্রেঞ্চ-নির্দিষ্ট নিয়ম
- ja.md # জাপানি-নির্দিষ্ট নিয়ম
- ar.md # আরবি-নির্দিষ্ট নিয়ম
+ _conventions.md # shared rules for all languages
+ fr.md # French-specific rules
+ ja.md # Japanese-specific rules
+ ar.md # Arabic-specific rules
```
-### সাধারণ কনভেনশন (`_conventions.md`)
+### শেয়ার্ড কনভেনশন (`_conventions.md`)
-সব ভাষার ক্ষেত্রে প্রযোজ্য নিয়মগুলো সংজ্ঞায়িত করুন: ইন্টারপোলেশন সিনট্যাক্স, HTML সংরক্ষণ, স্ট্রিং টাইপ কনভেনশন (বাটন বনাম লেবেল বনাম এরর) ইত্যাদি।
+সমস্ত ভাষার ক্ষেত্রে প্রযোজ্য নিয়মগুলো সংজ্ঞায়িত করুন: ইন্টারপোলেশন সিনট্যাক্স, HTML অক্ষুণ্ণ রাখা, স্ট্রিংয়ের ধরনের নিয়ম (বাটন বনাম লেবেল বনাম এরর) ইত্যাদি।
### ভাষাভিত্তিক গাইড (`{locale}.md`)
-ভাষা-নির্দিষ্ট নিয়মগুলো সংজ্ঞায়িত করুন: ফর্মালিটি রেজিস্টার (tu বনাম vous), বিরামচিহ্ন (guillemets, উল্টানো প্রশ্নবোধক চিহ্ন), বহুবচন রূপ, তারিখ/সংখ্যার ফরম্যাটিং এবং পরিভাষার গ্লোসারি।
+ভাষা-নির্দিষ্ট নিয়মগুলো নির্ধারণ করুন: ফর্মালিটির মাত্রা (tu বনাম vous), বিরামচিহ্ন (guillemets, উল্টানো প্রশ্নবোধক চিহ্ন), বহুবচনের রূপ, তারিখ/সংখ্যার ফরম্যাটিং এবং পরিভাষার গ্লোসারি।
-একটি কার্যকরী উদাহরণের জন্য [`examples/react-app/style-guides/`](examples/react-app/style-guides/) দেখুন।
+স্টাইল গাইডগুলো স্থায়ী পলিসি ইনপুট, কোনো জেনারেট করা আউটপুট নয়। Internationalizer এগুলো পড়ে কিন্তু কখনোই পরিবর্তন করে না। এদের বিষয়বস্তু গ্লোসারি এবং প্রম্পট চুক্তি থেকে আলাদাভাবে হ্যাশ করা হয়, ফলে অ্যাপ্লিকেশনের কোড পরিবর্তিত হলেও অনুবাদ পুরানো (stale) হয়ে যায় না। একটি গাইড ইচ্ছাকৃতভাবে এডিট করলে সেই লোকেলটি পলিসি পর্যালোচনার জন্য চিহ্নিত হয়; তবে অভ্যন্তরীণ প্রম্পটের ভাষা বদলালে এমনটা ঘটে না, যদি না প্রম্পট চুক্তির সংস্করণও পরিবর্তিত হয়।
+কার্যকর উদাহরণের জন্য [`examples/react-app/style-guides/`](../../examples/react-app/style-guides/) দেখুন।
+
## গ্লোসারি ফরম্যাট
-গ্লোসারি ফাইলগুলো হলো JSON অ্যারে যা `{glossary_dir}/{locale}.json`-এ সংরক্ষিত থাকে:
+গ্লোসারি ফাইলগুলো হলো JSON অ্যারে, যা `{glossary_dir}/{locale}.json`-এ সংরক্ষণ করা হয়:
```json
[
{
"source": "Dashboard",
"target": "Tableau de bord",
+ "variants": ["Panneau de contrôle"],
+ "enforcement": "error",
"ignore_case": false,
"whole_word": true
}
]
```
-টার্মগুলো একটি পরিভাষা টেবিল হিসেবে LLM প্রম্পটে ইনজেক্ট করা হয়, যা আপনার অ্যাপ্লিকেশনের মূল টার্মগুলোর সামঞ্জস্যপূর্ণ অনুবাদ নিশ্চিত করে।
-
+`variants` অনুমোদিত অন্যান্য টার্গেট রূপগুলোর তালিকা রাখে। `enforcement`-এর মান `error` বা `warning` হতে পারে, অথবা ডিফল্ট error আচরণের জন্য এটি বাদ দেওয়া যেতে পারে। পরিভাষার একটি টেবিল হিসেবে টার্মগুলো LLM প্রম্পটে যুক্ত হয়, যা আপনার পুরো অ্যাপ্লিকেশনে সামঞ্জস্যপূর্ণ অনুবাদ নিশ্চিত করে। `{"source":"API","target":"API"}`-এর মতো একটি অবিকল এন্ট্রি সম্পূর্ণ সোর্স-অভিন্ন মানটিকে স্ট্রিক্ট অনুবাদের ফাইন্ডিং থেকে অব্যাহতি দেয়; তবে এটি শুধুমাত্র `API` ধারণকারী কোনো দীর্ঘ বাক্যাংশকে অব্যাহতি দেয় না।
+
## ট্রান্সলেশন মেমরি
-ট্রান্সলেশন মেমরি একটি JSONL ফাইল হিসেবে সংরক্ষিত থাকে (প্রতি লাইনে একটি JSON রেকর্ড)। প্রতিটি রেকর্ডে থাকে:
+ট্রান্সলেশন মেমরি একটি JSONL ফাইল (প্রতি লাইনে একটি করে JSON রেকর্ড) হিসেবে সংরক্ষিত হয়। প্রতিটি রেকর্ডে থাকে:
-- সোর্স কি এবং ভ্যালু
-- অনুবাদ করা ভ্যালু
-- সোর্স ভ্যালুর একটি SHA-256 হ্যাশ
+- বান্ডেল, কি, সোর্স মান, অনুদিত মান এবং ক্যানোনিকাল টার্গেট লোকেল
+- সোর্স, স্টাইল গাইড, গ্লোসারি, প্রম্পট চুক্তি এবং সমন্বিত পলিসি হ্যাশ
+- যে প্রোভাইডার এবং মডেল অনুবাদটি তৈরি করেছে
- একটি টাইমস্ট্যাম্প
-পরবর্তী রানগুলোতে, অপরিবর্তিত স্ট্রিংগুলো LLM কল না করেই TM ক্যাশ থেকে সার্ভ করা হয়, যা সময় এবং API খরচ উভয়ই বাঁচায়। TM ফাইলটি গিট-বান্ধব (git-friendly) এবং আপনার লোকেল ফাইলগুলোর পাশাপাশি কমিট করা যেতে পারে।
-
+পরবর্তী রানগুলোতে, একই সোর্স ও পলিসি হ্যাশযুক্ত স্ট্রিংগুলো পুনরায় LLM-কে কল না করে ক্যাশ থেকে পরিবেশিত হয়। ডিফল্ট পাথ হলো গিট দ্বারা উপেক্ষিত `.internationalizer/` ডিরেক্টরি, যাতে এটি লোকাল ক্যাশ হিসেবে থাকে। আপনার প্রজেক্ট যদি দলগতভাবে ট্রান্সলেশন মেমরি শেয়ার করতে চায়, তবে `tm_path`-কে একটি ট্র্যাক করা লোকেশনে সেট করুন। পর্যালোচনাযোগ্য `.internationalizer.lock` ম্যানিফেস্টটি পৃথকভাবে সংস্করণ নিয়ন্ত্রিত হয়।
+
## সাপোর্টেড ফরম্যাট
| ফরম্যাট | এক্সটেনশন | মোড |
|--------|-----------|------|
-| JSON | `.json` | কি-ভ্যালু (নেস্টেড, ডট-নোটেশন ফ্ল্যাটেনড) |
-| YAML | `.yml`, `.yaml` | কি-ভ্যালু (কমেন্ট এবং ক্রম সংরক্ষণ করে) |
-| Markdown | `.md`, `.mdx` | সম্পূর্ণ-ডকুমেন্ট অনুবাদ |
+| JSON | `.json` | কি-ভ্যালু (নেস্টেড, ডট-নোটেশন দ্বারা ফ্ল্যাটেন করা) |
+| YAML | `.yml`, `.yaml` | কি-ভ্যালু (মন্তব্য এবং ক্রম অক্ষুণ্ণ রাখে) |
+| Markdown | `.md`, `.mdx` | প্রস্তাবনা (preamble) এবং H2-স্তরের বিভাগসমূহ |
+Markdown টার্গেটে H2 বিভাগের পূর্বে অদৃশ্য `internationalizer:unit` কমেন্ট থাকে। এই অপরিবর্তনশীল মার্কারগুলোর সাহায্যে Internationalizer অন্য কোনো অসম্পর্কিত অংশ পুনরায় অনুবাদ না করেই সোর্সের নির্দিষ্ট একটি বিভাগ যোগ, সরানো বা এডিট করতে পারে। বিদ্যমান মার্কারবিহীন ডকুমেন্টগুলোতে পরবর্তী সফল আপডেটের সময় মার্কার যোগ করা হয়।
+
## প্রজেক্ট টাইপ ডিটেকশন
-`internationalizer detect` নিচের বিষয়গুলো চেক করে আপনার i18n সেটআপ শনাক্ত করে:
-
-- react-i18next, next-intl, অথবা vue-i18n-এর জন্য `package.json` ডিপেন্ডেন্সি
-- সাধারণ লোকেল প্যাটার্নের সাথে মিলে যাওয়া ডিরেক্টরি স্ট্রাকচার
-- ফাইল এক্সটেনশন এবং নেমিং কনভেনশন
+`internationalizer detect` নিচের বিষয়গুলো পরীক্ষা করে আপনার i18n সেটআপ শনাক্ত করে:
+- react-i18next, next-intl, অথবা vue-i18n-এর জন্য `package.json`-এর ডিপেন্ডেন্সি
+- প্রচলিত লোকেল কাঠামোর সাথে মিলে যাওয়া ডিরেক্টরি প্যাটার্ন
+- ফাইলের এক্সটেনশন এবং নামকরণের নিয়ম
+
## আর্কিটেকচার
```
-cmd/internationalizer/ CLI এন্ট্রি পয়েন্ট এবং কমান্ড ডেফিনিশন
+cmd/internationalizer/ CLI entry point and command definitions
internal/
- config/ ডিফল্টসহ YAML কনফিগ লোডিং
- detect/ প্রজেক্ট টাইপ অটো-ডিটেকশন
- formats/ ফরম্যাট পার্সার (JSON, YAML, Markdown)
- glossary/ ভাষাভিত্তিক গ্লোসারি ম্যানেজমেন্ট
- llm/ LLM প্রোভাইডার ইন্টারফেস + ইমপ্লিমেন্টেশন
- anthropic.go Anthropic Claude ব্যাকএন্ড
- openai.go OpenAI / সামঞ্জস্যপূর্ণ ব্যাকএন্ড
- gemini.go AI Studio-এর মাধ্যমে Google Gemini ব্যাকএন্ড
- OpenRouter কাস্টম base_url-এর সাথে openai.go ব্যবহার করে
- styleguide/ স্টাইল গাইড লোডার
- tm/ JSONL ট্রান্সলেশন মেমরি
- translate/ ট্রান্সলেশন অর্কেস্ট্রেটর
- validate/ লোকেল ভ্যালিডেশন এবং ডিফিং
+ config/ YAML config loading with defaults
+ detect/ Project type auto-detection
+ formats/ Format parsers (JSON, YAML, Markdown)
+ glossary/ Per-locale glossary management
+ llm/ LLM provider interface + implementations
+ anthropic.go Anthropic Claude backend
+ openai.go OpenAI / compatible backend
+ gemini.go Google Gemini via AI Studio backend
+ OpenRouter uses openai.go with custom base_url
+ locale/ BCP 47 identity and CLDR plural categories
+ message/ ICU MessageFormat parser and structural comparison
+ policy/ Stable translation-policy hashing
+ state/ Versioned translation manifest
+ styleguide/ Style guide loader
+ tm/ JSONL translation memory
+ translate/ Translation orchestrator
+ validate/ Locale validation and diffing
```
-
+
## বিকল্পগুলোর সাথে তুলনা
| ফিচার | Internationalizer | i18next | Crowdin | সাধারণ LLM |
@@ -299,16 +395,17 @@ internal/
| ভাষাভিত্তিক স্টাইল গাইড | হ্যাঁ | না | না | না |
| গ্লোসারি প্রয়োগ | হ্যাঁ | না | হ্যাঁ | না |
| ট্রান্সলেশন মেমরি | হ্যাঁ | না | হ্যাঁ | না |
-| CLI / লোকাল এক্সিকিউশন | হ্যাঁ | প্রযোজ্য নয় | না | ম্যানুয়াল |
-| গিট-বান্ধব ফাইল | হ্যাঁ | হ্যাঁ | আংশিক | ম্যানুয়াল |
+| CLI / লোকাল রান | হ্যাঁ | প্রযোজ্য নয় | না | ম্যানুয়াল |
+| Git-বান্ধব ফাইল | হ্যাঁ | হ্যাঁ | আংশিক | ম্যানুয়াল |
| কোনো SaaS নির্ভরতা নেই | হ্যাঁ | হ্যাঁ | না | ভিন্ন হয় |
| ওপেন সোর্স (AGPL-3.0) | হ্যাঁ | হ্যাঁ | না | ভিন্ন হয় |
-
+
## লাইসেন্স
-[AGPL-3.0](LICENSE)
+[AGPL-3.0](../../LICENSE)
+ডিপেন্ডেন্সি সংক্রান্ত বিজ্ঞপ্তির জন্য [THIRD_PARTY_NOTICES.md](../../THIRD_PARTY_NOTICES.md) দেখুন।
+
## কন্ট্রিবিউটিং
-ডেভেলপমেন্ট সেটআপ এবং নির্দেশিকার জন্য [CONTRIBUTING.md](CONTRIBUTING.md) দেখুন। সব কন্ট্রিবিউশনের জন্য DCO সাইন-অফ প্রয়োজন।
-
+ডেভেলপমেন্ট সেটআপ এবং নির্দেশিকার জন্য [CONTRIBUTING.md](../../CONTRIBUTING.md) দেখুন। সমস্ত অবদানের ক্ষেত্রে DCO সাইন-অফ বাধ্যতামূলক।
diff --git a/docs/i18n/cs.md b/docs/i18n/cs.md
index 6de6af9..5de2a16 100644
--- a/docs/i18n/cs.md
+++ b/docs/i18n/cs.md
@@ -9,30 +9,29 @@
AI nativní internacionalizační pipeline pro softwarové projekty. Překládejte, ověřujte a spravujte soubory i18n pomocí LLM.
[](https://github.com/Tom-R-Main/Internationalizer/actions/workflows/ci.yml)
-[](LICENSE)
+[](../../LICENSE)
-العربية · বাংলা · Deutsch · Español · Français · हिन्दी · Indonesia · 日本語 · ਪੰਜਾਬੀ · Português · Русский · తెలుగు
-ไทย · Українська · 粵語 · 简体中文 · 繁體中文
+العربية · বাংলা · Čeština · Dansk · Deutsch · Ελληνικά · Español · Suomi · Français · עברית · हिन्दी · Indonesia · Italiano · 日本語 · 한국어 · Bahasa Melayu
Nederlands · ਪੰਜਾਬੀ · Polski · Português · Română · Русский · Svenska · తెలుగు · ไทย · Türkçe · Українська · Tiếng Việt · 粵語 · 简体中文 · 繁體中文
---
-
+
## Proč Internationalizer?
-Většina nástrojů pro i18n jsou buď runtime knihovny (i18next, react-intl), nebo SaaS platformy pro správu klíčů (Crowdin, Lokalise). Žádný z nich však dobře neřeší samotný problém s překladem:
-
-- **Manuální překlad** nelze efektivně škálovat pro více než několik jazyků.
-- **API pro strojový překlad** (Google Translate, DeepL) ignorují vaši terminologii, tón a konvence uživatelského rozhraní.
-- **Obecný překlad pomocí LLM** funguje lépe, ale bez glosářů a stylistických příruček získáte nekonzistentní výsledky.
+Většina nástrojů pro i18n představuje buď runtime knihovny (i18next, react-intl), nebo SaaS platformy pro správu klíčů (Crowdin, Lokalise). Žádný z nich však skutečný problém s překladem neřeší uspokojivě:
-Internationalizer je jiný. Je to **CLI pipeline**, která kombinuje překlad pomocí LLM s následujícími funkcemi:
+- **Ruční překlad** nelze škálovat pro více než několik jazyků
+- **Rozhraní API pro strojový překlad** (Google Translate, DeepL) ignorují vaši terminologii, tón i konvence uživatelského rozhraní
+- **Běžný překlad pomocí LLM** funguje lépe, ale bez glosářů a stylistických příruček vede k nekonzistentním výsledkům
-- **Glosáře pro jednotlivé jazyky** — vynucují konzistentní terminologii v celé aplikaci.
-- **Stylistické příručky pro jednotlivé jazyky** — řídí tón, formálnost, pluralizaci a typografii.
-- **Překladová paměť** — přeskakuje nezměněné řetězce a šetří peníze za volání API.
-- **Ověřování klíčů** — odhalí chybějící překlady a neshody v interpolaci ještě před vydáním.
+Internationalizer se liší. Představuje **CLI pipeline**, která spojuje překlad pomocí LLM s těmito prvky:
+- **Glosáře pro jednotlivé jazyky** – vynucují jednotnou terminologii v celé aplikaci
+- **Stylistické příručky pro jednotlivé jazyky** – řídí tón, formálnost, pravidla plurálu a typografii
+- **Překladová paměť** – přeskakuje nezměněné řetězce a šetří náklady na volání API
+- **Deterministické ověřování** – zachytí chybějící i přebývající klíče, porušení chráněných struktur, odchylky od glosáře a chyby v plurálech či syntaxi ICU ještě před nasazením
+
## Instalace
Instalace přes npm:
@@ -47,9 +46,9 @@ Nebo spuštění bez globální instalace:
npx internationalizer --help
```
-Balíček npm nainstaluje odpovídající předkompilovanou binární verzi z npm prostřednictvím volitelných závislostí specifických pro danou platformu.
+Balíček npm nainstaluje odpovídající předkompilovanou binárku z npm prostřednictvím volitelných závislostí pro konkrétní platformu.
-Instalace pomocí Go:
+Instalace v Go:
```bash
go install github.com/Tom-R-Main/Internationalizer/cmd/internationalizer@latest
@@ -62,23 +61,27 @@ git clone https://github.com/Tom-R-Main/Internationalizer.git
cd Internationalizer
go build -o internationalizer ./cmd/internationalizer
```
-
+
## Balíčky npm
-- Značky (tags) v Gitu a verze balíčků npm se musí shodovat, například `v0.1.0` a `0.1.0`.
-- Kořenový balíček `internationalizer` závisí na balíčcích pro konkrétní platformy, jako je `internationalizer-darwin-arm64`.
-- Podporované cíle npm: macOS arm64/x64, Linux arm64/x64, Windows x64.
-- Publikování přes CI vyžaduje GitHub secret s názvem `NPM_TOKEN`.
-
+- Značky v Gitu a verze balíčků npm se musí přesně shodovat, například `v0.1.0` a `0.1.0`
+- Kořenový balíček `internationalizer` závisí na platformních balíčcích, jako je `internationalizer-darwin-arm64`
+- Podporované cíle npm: macOS arm64/x64, Linux arm64/x64, Windows x64
+- Publikování přes CI vyžaduje secret v GitHubu s názvem `NPM_TOKEN`
+
## Rychlý start
-1. Vytvořte konfigurační soubor v kořenovém adresáři projektu:
+1. V kořenovém adresáři projektu vytvořte konfigurační soubor:
```yaml
# .internationalizer.yml
source_locale: en
target_locales: [fr, de, es, ja]
-source_path: locales/en.json
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
llm:
provider: gemini
@@ -86,13 +89,13 @@ llm:
api_key_env: GOOGLE_AI_STUDIO_API_KEY
```
-2. Nastavte svůj klíč API:
+2. Nastavte klíč rozhraní API:
```bash
export GOOGLE_AI_STUDIO_API_KEY=your-ai-studio-key
```
-3. Zobrazte si náhled toho, co se bude překládat:
+3. Zobrazte si náhled chystaného překladu:
```bash
internationalizer translate --dry-run
@@ -104,39 +107,67 @@ internationalizer translate --dry-run
internationalizer translate
```
-5. Ověřte všechny lokalizace:
+5. Zkontrolujte všechny lokalizace:
```bash
internationalizer validate
```
-
+
## Příkazy
### `translate`
-Najde chybějící klíče a přeloží je pomocí LLM.
+Vyhledá chybějící nebo zastaralé klíče a přeloží je prostřednictvím LLM.
```bash
internationalizer translate # translate all locales
internationalizer translate -l fr # translate French only
internationalizer translate --dry-run # preview without API calls
+internationalizer translate --adopt-existing # baseline existing translations without API calls
+internationalizer translate --refresh-policy # refresh prompt/style/model-stale entries
internationalizer translate --batch-size 20 # smaller batches
internationalizer translate --concurrency 2 # fewer parallel calls
```
+Stav překladu nezávisle zaznamenává chybějící řetězce, zastaralý zdroj, zastaralá pravidla, aktuální stav a ručně upravené záznamy, takže ruční úprava nemůže zakrýt změnu zdroje ani pravidel. Hodnoty se zastaralými pravidly se hlásí, ale znovu překládají pouze s přepínačem `--refresh-policy`. Ručně upravené hodnoty se nikdy automaticky nepřepíší. Přepínač `--adopt-existing` použijte při zavádění manifestu pro již zkontrolované překlady nebo při výslovném přijetí zkontrolované ruční úpravy jako nového výchozího stavu.
+
### `validate`
-Zkontroluje všechny soubory lokalizace na chybějící klíče, přebytečné klíče a neshody v interpolaci.
+Zkontroluje všechny soubory lokalizace oproti zdrojovým balíčkům. Výchozí ověření kontroluje strukturální pokrytí (procento přítomných požadovaných cílových klíčů), hlásí přebývající klíče jako varování a selže při chybějících klíčích, neshodách v interpolaci nebo neplatné struktuře ICU MessageFormat.
```bash
internationalizer validate # human-readable output
internationalizer validate --json # machine-readable JSON
internationalizer validate -q # exit code only
+internationalizer validate --strict # enforce translation quality rules
+internationalizer validate --require-state # require current manifest provenance
```
+Přepínač `--strict` navíc sleduje pokrytí překladu. Jazyková hodnota shodná se zdrojem se považuje za nepřeloženou, pokud glosář výslovně neobsahuje záznam se shodným zdrojem i cílem pro celou tuto hodnotu; volba `ignore_case` se respektuje, ale výskyt termínu z glosáře v delším textu výjimku netvoří. Striktní režim selže při přebývajících klíčích, hodnotách identických se zdrojem, změněné struktuře interpolace, HTML, kódu nebo odkazů v Markdownu, při porušení glosáře a chybějících konfigurovaných formách plurálu.
+
+Volba `--require-state` ověřuje každý cíl proti souboru `.internationalizer.lock`. Selže, pokud klíč není sledován nebo pokud je zastaralý záznam o zdroji, pravidlech překladu či hashi cíle. Lze ji kombinovat s volbou `--strict`.
+
+Textové i formátované JSON výstupy používají stálé kódy nálezů:
+
+| Kód | Význam |
+| --- | --- |
+| `missing_key` / `extra_key` | Sady zdrojových a cílových klíčů se liší |
+| `blank_translation` | Neprázdný zdroj má ve striktním režimu prázdný cíl |
+| `source_identical` | Jazyková hodnota zůstala ve striktním režimu nepřeložená |
+| `protected_structure_mismatch` | Změnila se struktura interpolace, kódů, značek HTML nebo odkazů |
+| `glossary_violation` | Nebyl nalezen žádný schválený cílový termín ani varianta |
+| `plural_form_missing` | Chybí nastavený tvar množného čísla pro danou lokalizaci |
+| `icu_message_syntax` | Zdrojová nebo cílová zpráva ICU má neplatnou syntaxi |
+| `icu_argument_mismatch` | Liší se názvy, typy nebo styly formátovačů argumentů ICU |
+| `icu_selector_mismatch` | Selektory se liší nebo je kategorie plurálu neplatná pro cílovou lokalizaci |
+| `untracked` | Pro cíl neexistuje v manifestu žádný záznam |
+| `source_stale` | Zdrojový obsah se od zaznamenaného překladu změnil |
+| `policy_stale` | Změnil se vygenerovaný prompt nebo nastavení modelu |
+| `target_modified` | Cílový obsah se neshoduje se záznamem v manifestu |
+
### `detect`
-Automaticky detekuje framework pro i18n a navrhne konfiguraci.
+Automaticky rozpozná framework pro i18n a navrhne konfiguraci.
```bash
internationalizer detect
@@ -146,7 +177,7 @@ Podporuje: react-i18next, next-intl, vue-i18n, čistý JSON, dokumentaci v Markd
### `glossary`
-Spravuje termíny v glosáři pro jednotlivé jazyky, které jsou vynucovány během překladu.
+Spravuje termíny glosáře pro jednotlivé jazyky, které jsou vynucovány během překladu.
```bash
internationalizer glossary list --locale fr
@@ -163,7 +194,7 @@ internationalizer tm stats # show record counts
internationalizer tm export # dump as JSON
internationalizer tm clear --force # delete all records
```
-
+
## Referenční příručka konfigurace
```yaml
@@ -173,10 +204,23 @@ internationalizer tm clear --force # delete all records
source_locale: en
# Languages to translate into (required)
-target_locales: [fr, de, es, ja, zh-CN, ar]
-
-# Path to the source locale file (required)
-source_path: locales/en.json
+target_locales: [fr, de, es, ja, yue, zh-CN, zh-TW, ar]
+
+# One or more source-to-target mappings (required).
+# {locale} is replaced with each configured target locale.
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
+ - id: docs
+ source: README.md
+ target: docs/i18n/{locale}.md
+ format: markdown
+
+# Backward compatibility: source_path still maps targets to sibling files
+# such as locales/fr.json. Prefer bundles for new projects.
+# source_path: locales/en.json
# LLM provider settings
llm:
@@ -185,7 +229,7 @@ llm:
# Model name defaults by provider:
# anthropic: claude-opus-5
- # openai: gpt-5.6-luna
+ # openai: gpt-5.6-luna (reasoning effort defaults to max)
# gemini: gemini-3.8-flash
# openrouter: deepseek/deepseek-v4-pro-0813
model: gemini-3.8-flash
@@ -196,6 +240,27 @@ llm:
# Base URL for OpenAI-compatible endpoints (optional)
# base_url: https://api.openai.com
+ # OpenAI GPT-5-series Responses API reasoning effort
+ # (default: max for the OpenAI provider)
+ reasoning_effort: max
+
+ # Optional LLM settings for individual target locales. An override using the
+ # global provider inherits unspecified global settings. A different provider
+ # uses that provider's defaults for unspecified settings.
+ locale_overrides:
+ yue:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-CN:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-TW:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+
# Keys per LLM call (default: 40)
batch_size: 40
@@ -210,11 +275,25 @@ glossary_dir: glossary
# Path to translation memory file (default: .internationalizer/tm.jsonl)
tm_path: .internationalizer/tm.jsonl
+
+# Versioned source, policy, target, and provenance state
+# (default: .internationalizer.lock; commit this file)
+manifest_path: .internationalizer.lock
+
+# Optional translation and strict-validation rules
+validation:
+ plural_style: i18next-v4 # generate and validate target-locale plural forms
```
+Identifikátory lokalizací musí být správně utvořené značky BCP 47, jako například `fr`, `pt-BR` nebo `sr-Latn-RS`. Kanonicky ekvivalentní cílové lokalizace jsou odmítnuty jako duplicity a specifická přepsání poskytovatelů se porovnávají podle kanonicky ekvivalentního zápisu. Ve výše uvedeném příkladu lokalizace bez přepsání – včetně japonštiny – dědí globální konfiguraci Gemini.
+
+Hodnoty ICU MessageFormat se analyzují strukturálně. Podporovány jsou jednoduché argumenty, `select`, `plural`, `selectordinal`, `number`, `date` a `time`, včetně vnořených zpráv, posunů plurálu (offsets), přesných číselných selektorů i znaku `#`. Validace kontroluje syntaxi, typy argumentů a styly formátovačů, posuny plurálu, identitu větví select a kategorie plurálu podle CLDR pro cílovou lokalizaci. Výstupy poskytovatelů porušující tyto invarianty jsou odmítnuty ještě před zápisem do souboru lokalizace nebo záznamu překladové paměti.
+
+Při použití `i18next-v4` se rozpoznané zdrojové rodiny plurálu při překladu rozšiřují na kategorie CLDR cílové lokalizace. Kategorie existující pouze v cíli používá jako šablonu překladu zdrojovou hodnotu `_other`. Striktní validace tyto cílové kategorie vyžaduje; kategorie přítomné pouze ve zdroji jsou pro cílové lokalizace, které je nepoužívají, volitelné.
+
## Stylistické příručky
-Stylistické příručky jsou soubory Markdown, které se vkládají do promptu pro překlad pomocí LLM. Řídí tón, formálnost, typografii a další konvence specifické pro daný jazyk.
+Stylistické příručky jsou soubory v Markdownu, které se vkládají do promptu pro překlad pomocí LLM. Určují tón, formálnost, typografii a další konvence specifické pro daný jazyk.
```
style-guides/
@@ -226,58 +305,64 @@ style-guides/
### Sdílené konvence (`_conventions.md`)
-Definují pravidla, která platí pro všechny jazyky: syntaxi interpolace, zachování HTML, konvence pro typy řetězců (tlačítka vs. štítky vs. chyby) atd.
+Definují pravidla platná pro všechny jazyky: syntaxi interpolace, zachování HTML, konvence pro typy řetězců (tlačítka vs. popisky vs. chyby) atd.
### Příručky pro jednotlivé jazyky (`{locale}.md`)
-Definují pravidla specifická pro daný jazyk: úroveň formálnosti (tykání vs. vykání), interpunkci (uvozovky, obrácené otazníky), tvary množného čísla, formátování data/čísel a terminologický glosář.
+Definují pravidla specifická pro daný jazyk: rovinu formálnosti (tykání vs. vykání), interpunkci (české uvozovky, obrácené otazníky), tvary množného čísla, formátování data a čísel a terminologický glosář.
-Funkční příklad najdete v [`examples/react-app/style-guides/`](examples/react-app/style-guides/).
+Stylistické příručky představují trvalé konfigurační vstupy, nikoli generovaný výstup. Internationalizer z nich čte, ale nikdy je nepřepisuje. Jejich obsah se hashuje odděleně od glosáře a kontraktu promptu, takže změna kódu aplikace nezpůsobí zastarání překladu. Úprava příručky záměrně označí danou lokalizaci k revizi pravidel; změna interní formulace promptu k tomu nevede, pokud se zároveň nezmění verze kontraktu promptu.
+Funkční příklad najdete v adresáři [`examples/react-app/style-guides/`](../../examples/react-app/style-guides/).
+
## Formát glosáře
-Soubory glosáře jsou pole ve formátu JSON uložená v `{glossary_dir}/{locale}.json`:
+Soubory glosáře jsou pole JSON uložená v souboru `{glossary_dir}/{locale}.json`:
```json
[
{
"source": "Dashboard",
"target": "Tableau de bord",
+ "variants": ["Panneau de contrôle"],
+ "enforcement": "error",
"ignore_case": false,
"whole_word": true
}
]
```
-Termíny jsou vloženy do promptu pro LLM jako terminologická tabulka, což zajišťuje konzistentní překlad klíčových termínů v celé aplikaci.
-
+Položka `variants` uvádí další schválené tvary v cílovém jazyce. `enforcement` může nabývat hodnot `error`, `warning`, nebo může být vynechána pro výchozí chování error. Termíny jsou vloženy do systémového promptu LLM jako tabulka terminologie, což zajišťuje konzistentní překlad v celé aplikaci. Přesný záznam, jako například `{"source":"API","target":"API"}`, navíc osvobozuje celou tuto se zdrojem shodnou hodnotu od striktních nálezů o nepřeloženém obsahu; neosvobozuje však delší hodnotu, která řetězec `API` pouze obsahuje.
+
## Překladová paměť
-Překladová paměť je uložena jako soubor JSONL (jeden záznam JSON na řádek). Každý záznam obsahuje:
+Překladová paměť je uložena v souboru JSONL (jeden záznam JSON na řádek). Každý záznam obsahuje:
-- Zdrojový klíč a hodnotu.
-- Přeloženou hodnotu.
-- Hash SHA-256 zdrojové hodnoty.
-- Časové razítko.
-
-Při dalších spuštěních jsou nezměněné řetězce načteny z mezipaměti překladové paměti (TM) bez volání LLM, což šetří čas i náklady na API. Soubor TM je vhodný pro verzování v Gitu a lze jej commitovat společně se soubory lokalizace.
+- Balíček, klíč, zdrojovou hodnotu, přeloženou hodnotu a kanonickou cílovou lokalizaci
+- Hashe zdroje, stylistické příručky, glosáře, kontraktu promptu a kombinovaných pravidel
+- Poskytovatele a model, které překlad vygenerovaly
+- Časové razítko
+Při dalších spuštěních se řetězce se stejným hashem zdroje i pravidel obsluhují z mezipaměti bez volání LLM. Výchozí cesta směřuje do ignorovaného adresáře `.internationalizer/`, takže zůstává lokální mezipamětí. Pokud projekt překladovou paměť záměrně sdílí, nastavte `tm_path` na sledované umístění. Revidovatelný manifest `.internationalizer.lock` se verzuje samostatně.
+
## Podporované formáty
| Formát | Přípony | Režim |
|--------|-----------|------|
-| JSON | `.json` | Klíč-hodnota (vnořené, zploštělé pomocí tečkové notace) |
-| YAML | `.yml`, `.yaml` | Klíč-hodnota (zachovává komentáře a pořadí) |
-| Markdown | `.md`, `.mdx` | Překlad celého dokumentu |
+| JSON | `.json` | Klíč-hodnota (vnořené, zploštělé tečkovou notací) |
+| YAML | `.yml`, `.yaml` | Klíč-hodnota (zachovává komentáře a řazení) |
+| Markdown | `.md`, `.mdx` | Úvodní část a oddíly na úrovni nadpisů H2 |
+Cíle v Markdownu obsahují před oddíly H2 neviditelné komentáře `internationalizer:unit`. Tyto stabilní značky umožňují nástroji Internationalizer přidat, přesunout nebo upravit jeden zdrojový oddíl bez nutnosti znovu překládat nesouvisející oddíly. Stávající neoznačené dokumenty tyto značky obdrží při příští úspěšné aktualizaci.
+
## Detekce typu projektu
-Příkaz `internationalizer detect` identifikuje vaše nastavení i18n kontrolou:
-
-- Závislostí v `package.json` pro react-i18next, next-intl nebo vue-i18n.
-- Struktur adresářů odpovídajících běžným vzorům lokalizace.
-- Přípon souborů a konvencí pojmenování.
+Příkaz `internationalizer detect` rozpozná nastavení vaší internacionalizace kontrolou:
+- Závislostí v `package.json` pro react-i18next, next-intl nebo vue-i18n
+- Adresářových struktur odpovídajících běžným vzorům lokalizace
+- Přípon souborů a konvencí pro pojmenování
+
## Architektura
```
@@ -292,30 +377,35 @@ internal/
openai.go OpenAI / compatible backend
gemini.go Google Gemini via AI Studio backend
OpenRouter uses openai.go with custom base_url
+ locale/ BCP 47 identity and CLDR plural categories
+ message/ ICU MessageFormat parser and structural comparison
+ policy/ Stable translation-policy hashing
+ state/ Versioned translation manifest
styleguide/ Style guide loader
tm/ JSONL translation memory
translate/ Translation orchestrator
validate/ Locale validation and diffing
```
-
+
## Srovnání s alternativami
-| Funkce | Internationalizer | i18next | Crowdin | Obecné LLM |
+| Funkce | Internationalizer | i18next | Crowdin | Běžné LLM |
|---------|------------------|---------|---------|-------------|
| Překlad pomocí LLM | Ano | Ne | Částečně | Ano |
| Stylistické příručky pro jednotlivé jazyky | Ano | Ne | Ne | Ne |
| Vynucování glosáře | Ano | Ne | Ano | Ne |
| Překladová paměť | Ano | Ne | Ano | Ne |
-| CLI / lokální spuštění | Ano | N/A | Ne | Manuálně |
-| Soubory vhodné pro Git | Ano | Ano | Částečně | Manuálně |
-| Bez závislosti na SaaS | Ano | Ano | Ne | Různé |
+| CLI / lokální spuštění | Ano | N/A | Ne | Ručně |
+| Soubory vhodné pro Git | Ano | Ano | Částečně | Ručně |
+| Žádná závislost na SaaS | Ano | Ano | Ne | Různé |
| Open source (AGPL-3.0) | Ano | Ano | Ne | Různé |
-
+
## Licence
-[AGPL-3.0](LICENSE)
+[AGPL-3.0](../../LICENSE)
+Oznámení o závislostech třetích stran najdete v souboru [THIRD_PARTY_NOTICES.md](../../THIRD_PARTY_NOTICES.md).
+
## Přispívání
-Pokyny pro nastavení vývojového prostředí a pravidla najdete v [CONTRIBUTING.md](CONTRIBUTING.md). Všechny příspěvky vyžadují schválení DCO (Developer Certificate of Origin).
-
+Pokyny pro nastavení vývojového prostředí a pravidla pro přispívání najdete v souboru [CONTRIBUTING.md](../../CONTRIBUTING.md). Všechny příspěvky vyžadují potvrzení DCO (Developer Certificate of Origin).
diff --git a/docs/i18n/da.md b/docs/i18n/da.md
index 254fce5..0b81cf8 100644
--- a/docs/i18n/da.md
+++ b/docs/i18n/da.md
@@ -9,17 +9,17 @@
AI-baseret internationaliserings-pipeline til softwareprojekter. Oversæt, valider og administrer i18n-filer ved hjælp af LLM'er.
[](https://github.com/Tom-R-Main/Internationalizer/actions/workflows/ci.yml)
-[](LICENSE)
+[](../../LICENSE)
-العربية · বাংলা · Deutsch · Español · Français · हिन्दी · Indonesia · 日本語 · ਪੰਜਾਬੀ · Português · Русский · తెలుగు
ไทย · Українська · 粵語 · 简体中文 · 繁體中文
+العربية · বাংলা · Čeština · Dansk · Deutsch · Ελληνικά · Español · Suomi · Français · עברית · हिन्दी · Indonesia · Italiano · 日本語 · 한국어 · Bahasa Melayu
Nederlands · ਪੰਜਾਬੀ · Polski · Português · Română · Русский · Svenska · తెలుగు · ไทย · Türkçe · Українська · Tiếng Việt · 粵語 · 简体中文 · 繁體中文
---
-
+
## Hvorfor Internationalizer?
-De fleste i18n-værktøjer er enten runtime-biblioteker (i18next, react-intl) eller SaaS-platforme til nøglehåndtering (Crowdin, Lokalise). Ingen af dem løser det egentlige oversættelsesproblem særlig godt:
+De fleste i18n-værktøjer er enten runtime-biblioteker (i18next, react-intl) eller SaaS-platforme til nøgleadministration (Crowdin, Lokalise). Ingen af dem løser det egentlige oversættelsesproblem særlig godt:
- **Manuel oversættelse** skalerer ikke ud over et par sprog
- **Maskinoversættelses-API'er** (Google Translate, DeepL) ignorerer din terminologi, tone og UI-konventioner
@@ -27,11 +27,11 @@ De fleste i18n-værktøjer er enten runtime-biblioteker (i18next, react-intl) el
Internationalizer er anderledes. Det er en **CLI-pipeline**, der kombinerer LLM-oversættelse med:
-- **Sprogspecifikke ordlister** — håndhæver konsistent terminologi på tværs af din app
-- **Sprogspecifikke stilguider** — styrer tone, formalitet, flertalsbøjning og typografi
-- **Oversættelseshukommelse** — springer uændrede strenge over og sparer penge på API-kald
-- **Nøglevalidering** — fanger manglende oversættelser og interpolationsfejl, før de udgives
-
+- **Sprogspecifikke ordlister** – håndhæv ensartet terminologi på tværs af din app
+- **Sprogspecifikke stilguider** – styr tone, formalitet, flertalsbøjning og typografi
+- **Oversættelseshukommelse** – spring uændrede strenge over, og spar penge på API-kald
+- **Deterministisk validering** – fang manglende eller ekstra nøgler, ændringer i beskyttet struktur, ordlisteproblemer samt flertals- eller ICU-fejl, før de udgives
+
## Installation
Installer fra npm:
@@ -46,7 +46,7 @@ Eller kør uden en global installation:
npx internationalizer --help
```
-npm-pakken installerer den matchende forudkompilerede binære fil fra npm via platformsspecifikke valgfrie afhængigheder.
+npm-pakken installerer den matchende forudbyggede binære fil fra npm via platformsspecifikke valgfrie afhængigheder.
Installer med Go:
@@ -61,15 +61,15 @@ git clone https://github.com/Tom-R-Main/Internationalizer.git
cd Internationalizer
go build -o internationalizer ./cmd/internationalizer
```
-
+
## npm-pakker
-- Git-tags og npm-pakkeversioner skal matche, for eksempel `v0.1.0` og `0.1.0`
-- Rodpakken `internationalizer` afhænger af platformspakker som `internationalizer-darwin-arm64`
+- Git-tags og npm-pakkeversioner skal stemme overens, for eksempel `v0.1.0` og `0.1.0`
+- Rodpakken `internationalizer` afhænger af platformspakker såsom `internationalizer-darwin-arm64`
- Understøttede npm-mål: macOS arm64/x64, Linux arm64/x64, Windows x64
-- CI-udgivelse kræver en GitHub-hemmelighed med navnet `NPM_TOKEN`
-
-## Kom hurtigt i gang
+- CI-udgivelse kræver en GitHub-hemmelighed ved navn `NPM_TOKEN`
+
+## Hurtig start
1. Opret en konfigurationsfil i din projektrod:
@@ -77,7 +77,11 @@ go build -o internationalizer ./cmd/internationalizer
# .internationalizer.yml
source_locale: en
target_locales: [fr, de, es, ja]
-source_path: locales/en.json
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
llm:
provider: gemini
@@ -108,31 +112,59 @@ internationalizer translate
```bash
internationalizer validate
```
-
+
## Kommandoer
### `translate`
-Find manglende nøgler, og oversæt dem via en LLM.
+Find manglende eller forældede nøgler, og oversæt dem via en LLM.
```bash
internationalizer translate # translate all locales
internationalizer translate -l fr # translate French only
internationalizer translate --dry-run # preview without API calls
+internationalizer translate --adopt-existing # baseline existing translations without API calls
+internationalizer translate --refresh-policy # refresh prompt/style/model-stale entries
internationalizer translate --batch-size 20 # smaller batches
internationalizer translate --concurrency 2 # fewer parallel calls
```
+Oversættelsesstatus rapporterer uafhængigt om manglende (missing), kildeforældede (source-stale), politikforældede (policy-stale), aktuelle (current) og manuelt redigerede tilstande, så en manuel redigering ikke kan skjule en kilde- eller politikændring. Politikforældede værdier rapporteres, men genoversættes kun med `--refresh-policy`. Manuelt redigerede værdier overskrives aldrig automatisk. Brug `--adopt-existing`, når du introducerer manifestet til gennemgåede oversættelser, eller når du udtrykkeligt accepterer en gennemgået manuel redigering som det nye udgangspunkt.
+
### `validate`
-Tjek alle sprogfiler for manglende nøgler, ekstra nøgler og interpolationsfejl.
+Kontroller alle sprogfiler mod deres kildebundter. Standardvalidering kontrollerer strukturel dækning (procentdelen af påkrævede målnøgler, der er til stede), rapporterer ekstra nøgler som advarsler og mislykkes ved manglende nøgler, uoverensstemmelser i interpolation eller ugyldig ICU MessageFormat-struktur.
```bash
internationalizer validate # human-readable output
internationalizer validate --json # machine-readable JSON
internationalizer validate -q # exit code only
+internationalizer validate --strict # enforce translation quality rules
+internationalizer validate --require-state # require current manifest provenance
```
+`--strict` rapporterer også oversat dækning. En sproglig værdi, der er identisk med sin kilde, betragtes som uoversat, medmindre ordlisten udtrykkeligt indeholder en nøjagtig post med samme kilde og samme mål for den fulde værdi; der tages højde for `ignore_case`, men et ordlistebegreb, der er indlejret i en længere værdi, udgør ikke en undtagelse. Strikt tilstand mislykkes ved ekstra nøgler, kildeidentiske værdier, ændret struktur for interpolation, HTML, kode eller Markdown-links, overtrædelser af ordlisten samt konfigurerede flertalsformer.
+
+`--require-state` verificerer hvert mål mod `.internationalizer.lock`. Den mislykkes, hvis en nøgle ikke spores, eller hvis dens registrerede kilde-, oversættelsespolitik- eller mål-hash er forældet. Den kan kombineres med `--strict`.
+
+Menneske- og JSON-rapporter bruger stabile resultatkoder:
+
+| Kode | Betydning |
+| --- | --- |
+| `missing_key` / `extra_key` | Kilde- og målnøglesæt er forskellige |
+| `blank_translation` | En ikketom kilde har et tomt mål i strikt tilstand |
+| `source_identical` | En sproglig værdi forbliver uoversat i strikt tilstand |
+| `protected_structure_mismatch` | Struktur for interpolation, HTML, kode eller links er ændret |
+| `glossary_violation` | Der blev ikke fundet noget godkendt målbegreb eller variant |
+| `plural_form_missing` | En konfigureret sprogspecifik flertalsform mangler |
+| `icu_message_syntax` | En kilde- eller mål-ICU-meddelelse er misdannet |
+| `icu_argument_mismatch` | ICU-argumentnavne, -typer eller formateringsstile er forskellige |
+| `icu_selector_mismatch` | Vælgere er forskellige, eller en flertalskategori er ugyldig for målsproget |
+| `untracked` | Der findes ingen manifestpost for målet |
+| `source_stale` | Kildeindholdet blev ændret efter den registrerede oversættelse |
+| `policy_stale` | Den genererede prompt eller modelindstillingerne blev ændret |
+| `target_modified` | Målindholdet afviger fra manifestposten |
+
### `detect`
Registrer automatisk i18n-frameworket, og foreslå en konfiguration.
@@ -141,11 +173,11 @@ Registrer automatisk i18n-frameworket, og foreslå en konfiguration.
internationalizer detect
```
-Understøtter: react-i18next, next-intl, vue-i18n, ren JSON, markdown-dokumenter.
+Understøtter: react-i18next, next-intl, vue-i18n, ren JSON, Markdown-dokumenter.
### `glossary`
-Administrer sprogspecifikke ordlistetermer, der håndhæves under oversættelsen.
+Administrer sprogspecifikke ordlistebegreber, der håndhæves under oversættelse.
```bash
internationalizer glossary list --locale fr
@@ -155,14 +187,14 @@ internationalizer glossary remove --locale fr --source "Dashboard"
### `tm`
-Administrer oversættelseshukommelse (JSONL-cache af tidligere oversatte strenge).
+Administrer oversættelseshukommelse (JSONL-cache med tidligere oversatte strenge).
```bash
internationalizer tm stats # show record counts
internationalizer tm export # dump as JSON
internationalizer tm clear --force # delete all records
```
-
+
## Konfigurationsreference
```yaml
@@ -172,10 +204,23 @@ internationalizer tm clear --force # delete all records
source_locale: en
# Languages to translate into (required)
-target_locales: [fr, de, es, ja, zh-CN, ar]
-
-# Path to the source locale file (required)
-source_path: locales/en.json
+target_locales: [fr, de, es, ja, yue, zh-CN, zh-TW, ar]
+
+# One or more source-to-target mappings (required).
+# {locale} is replaced with each configured target locale.
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
+ - id: docs
+ source: README.md
+ target: docs/i18n/{locale}.md
+ format: markdown
+
+# Backward compatibility: source_path still maps targets to sibling files
+# such as locales/fr.json. Prefer bundles for new projects.
+# source_path: locales/en.json
# LLM provider settings
llm:
@@ -184,7 +229,7 @@ llm:
# Model name defaults by provider:
# anthropic: claude-opus-5
- # openai: gpt-5.6-luna
+ # openai: gpt-5.6-luna (reasoning effort defaults to max)
# gemini: gemini-3.8-flash
# openrouter: deepseek/deepseek-v4-pro-0813
model: gemini-3.8-flash
@@ -195,6 +240,27 @@ llm:
# Base URL for OpenAI-compatible endpoints (optional)
# base_url: https://api.openai.com
+ # OpenAI GPT-5-series Responses API reasoning effort
+ # (default: max for the OpenAI provider)
+ reasoning_effort: max
+
+ # Optional LLM settings for individual target locales. An override using the
+ # global provider inherits unspecified global settings. A different provider
+ # uses that provider's defaults for unspecified settings.
+ locale_overrides:
+ yue:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-CN:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-TW:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+
# Keys per LLM call (default: 40)
batch_size: 40
@@ -209,8 +275,22 @@ glossary_dir: glossary
# Path to translation memory file (default: .internationalizer/tm.jsonl)
tm_path: .internationalizer/tm.jsonl
+
+# Versioned source, policy, target, and provenance state
+# (default: .internationalizer.lock; commit this file)
+manifest_path: .internationalizer.lock
+
+# Optional translation and strict-validation rules
+validation:
+ plural_style: i18next-v4 # generate and validate target-locale plural forms
```
+Sprog-id'er skal være velformede BCP 47-tags såsom `fr`, `pt-BR` eller `sr-Latn-RS`. Kanonisk ækvivalente målsprog afvises som dubletter, og sprogspecifikke tilsidesættelser for udbydere matcher kanonisk ækvivalent stavning. I eksemplet ovenfor arver sprog uden en tilsidesættelse – herunder japansk – den globale Gemini-konfiguration.
+
+ICU MessageFormat-værdier parses strukturelt. Simple argumenter, `select`, `plural`, `selectordinal`, `number`, `date` og `time` understøttes, herunder indlejrede meddelelser, flertalsforskydninger, eksakte talvælgere og `#`. Valideringen kontrollerer syntaks, argumenttyper og formateringsstile, flertalsforskydninger, select-forgreningsidentitet og målsprogets CLDR-flertalskategorier. Udbyderoutput, der bryder disse invarianter, afvises, før der skrives en sprogfil eller en post i oversættelseshukommelsen.
+
+Med `i18next-v4` udvides genkendte kildeflertalsfamilier under oversættelsen til målsprogets CLDR-kategorier. En kategori, der kun findes i målet, bruger kildefamiliens `_other`-værdi som sin oversættelsesskabelon. Strikt validering kræver disse målkategorier; kategorier, der kun findes i kilden, er valgfrie for målsprog, der ikke anvender dem.
+
## Stilguider
Stilguider er Markdown-filer, der injiceres i LLM-oversættelsesprompten. De styrer tone, formalitet, typografi og andre sprogspecifikke konventioner.
@@ -223,16 +303,18 @@ style-guides/
ar.md # Arabic-specific rules
```
-### Delte konventioner (`_conventions.md`)
+### Fælles konventioner (`_conventions.md`)
Definer regler, der gælder for alle sprog: interpolationssyntaks, bevarelse af HTML, konventioner for strengtyper (knapper vs. etiketter vs. fejl) osv.
### Sprogspecifikke guider (`{locale}.md`)
-Definer sprogspecifikke regler: formalitetsregister (tu vs. vous), tegnsætning (vinkelanførselstegn, omvendte spørgsmålstegn), flertalsformer, dato-/talformatering og en terminologiordliste.
+Definer sprogspecifikke regler: formalitetsregister (du vs. De), tegnsætning (anførselstegn, omvendte spørgsmålstegn), flertalsformer, dato-/talformatering og en terminologiordliste.
-Se [`examples/react-app/style-guides/`](examples/react-app/style-guides/) for et fungerende eksempel.
+Stilguider er varige politiske input, ikke genereret output. Internationalizer læser dem, men omskriver dem aldrig. Deres indhold hashes separat fra ordlisten og promptkontrakten, så en ændring i applikationskoden ikke gør en oversættelse forældet. Redigering af en guide markerer bevidst det pågældende sprog til politikgennemgang; ændring af intern promptordlyd gør ikke, medmindre versionen af promptkontrakten også ændres.
+Se [`examples/react-app/style-guides/`](../../examples/react-app/style-guides/) for et fungerende eksempel.
+
## Ordlisteformat
Ordlistefiler er JSON-arrays, der gemmes i `{glossary_dir}/{locale}.json`:
@@ -242,41 +324,45 @@ Ordlistefiler er JSON-arrays, der gemmes i `{glossary_dir}/{locale}.json`:
{
"source": "Dashboard",
"target": "Tableau de bord",
+ "variants": ["Panneau de contrôle"],
+ "enforcement": "error",
"ignore_case": false,
"whole_word": true
}
]
```
-Termer injiceres i LLM-prompten som en terminologitabel, hvilket sikrer konsistent oversættelse af nøgletermer på tværs af din applikation.
-
+`variants` angiver andre godkendte målformer. `enforcement` kan være `error`, `warning` eller udelades for standardadfærden med fejl. Begreber injiceres i LLM-prompten som en terminologitabel, hvilket sikrer ensartet oversættelse på tværs af din applikation. En nøjagtig post som f.eks. `{"source":"API","target":"API"}` fritager også den komplette kildeidentiske værdi for fund af uoversatte værdier i strikt tilstand; den fritager ikke en længere værdi, der blot indeholder `API`.
+
## Oversættelseshukommelse
Oversættelseshukommelse gemmes som en JSONL-fil (én JSON-post pr. linje). Hver post indeholder:
-- Kildenøglen og -værdien
-- Den oversatte værdi
-- En SHA-256-hash af kildeværdien
+- Bundtet, nøglen, kildeværdien, den oversatte værdi og det kanoniske målsprog
+- Hashes for kilde, stilguide, ordliste, promptkontrakt og kombineret politik
+- Den udbyder og model, der producerede oversættelsen
- Et tidsstempel
-Ved efterfølgende kørsler leveres uændrede strenge fra TM-cachen uden at kalde LLM'en, hvilket sparer både tid og API-omkostninger. TM-filen er Git-venlig og kan committes sammen med dine sprogfiler.
-
+Ved efterfølgende kørsler leveres strenge med de samme kilde- og politik-hashes fra cachen uden at kalde LLM'en. Standardstien er under den ignorerede mappe `.internationalizer/`, så den forbliver en lokal cache. Angiv `tm_path` til en sporet placering, hvis dit projekt bevidst deler oversættelseshukommelse. Det gennemgåelige `.internationalizer.lock`-manifest versioneres separat.
+
## Understøttede formater
| Format | Udvidelser | Tilstand |
-|--------|-----------|------|
+| --- | --- | --- |
| JSON | `.json` | Nøgle-værdi (indlejret, fladgjort med punktumnotation) |
| YAML | `.yml`, `.yaml` | Nøgle-værdi (bevarer kommentarer og rækkefølge) |
-| Markdown | `.md`, `.mdx` | Oversættelse af hele dokumentet |
+| Markdown | `.md`, `.mdx` | Præambel og afsnit på H2-niveau |
+Markdown-mål indeholder usynlige `internationalizer:unit`-kommentarer før H2-afsnit. Disse stabile markører gør det muligt for Internationalizer at tilføje, flytte eller redigere et enkelt kildeafsnit uden at genoversætte urelaterede afsnit. Eksisterende uafmærkede dokumenter modtager markører ved deres næste vellykkede opdatering.
+
## Registrering af projekttype
-`internationalizer detect` identificerer din i18n-opsætning ved at tjekke:
+`internationalizer detect` identificerer din i18n-opsætning ved at kontrollere:
- `package.json`-afhængigheder for react-i18next, next-intl eller vue-i18n
- Mappestrukturer, der matcher almindelige sprogmønstre
- Filudvidelser og navngivningskonventioner
-
+
## Arkitektur
```
@@ -291,30 +377,35 @@ internal/
openai.go OpenAI / compatible backend
gemini.go Google Gemini via AI Studio backend
OpenRouter uses openai.go with custom base_url
+ locale/ BCP 47 identity and CLDR plural categories
+ message/ ICU MessageFormat parser and structural comparison
+ policy/ Stable translation-policy hashing
+ state/ Versioned translation manifest
styleguide/ Style guide loader
tm/ JSONL translation memory
translate/ Translation orchestrator
validate/ Locale validation and diffing
```
-
+
## Sammenligning med alternativer
| Funktion | Internationalizer | i18next | Crowdin | Generisk LLM |
-|---------|------------------|---------|---------|-------------|
+| --- | --- | --- | --- | --- |
| LLM-drevet oversættelse | Ja | Nej | Delvist | Ja |
| Sprogspecifikke stilguider | Ja | Nej | Nej | Nej |
| Håndhævelse af ordliste | Ja | Nej | Ja | Nej |
| Oversættelseshukommelse | Ja | Nej | Ja | Nej |
-| CLI / lokal kørsel | Ja | N/A | Nej | Manuelt |
+| CLI / lokal udførelse | Ja | Ikke relevant | Nej | Manuelt |
| Git-venlige filer | Ja | Ja | Delvist | Manuelt |
| Ingen SaaS-afhængighed | Ja | Ja | Nej | Varierer |
| Open source (AGPL-3.0) | Ja | Ja | Nej | Varierer |
-
+
## Licens
-[AGPL-3.0](LICENSE)
+[AGPL-3.0](../../LICENSE)
+Se [THIRD_PARTY_NOTICES.md](../../THIRD_PARTY_NOTICES.md) for meddelelser om afhængigheder.
+
## Bidrag
-Se [CONTRIBUTING.md](CONTRIBUTING.md) for opsætning af udviklingsmiljø og retningslinjer. Alle bidrag kræver DCO-godkendelse.
-
+Se [CONTRIBUTING.md](../../CONTRIBUTING.md) for opsætning af udviklingsmiljø og retningslinjer. Alle bidrag kræver DCO-godkendelse.
diff --git a/docs/i18n/de.md b/docs/i18n/de.md
index 617630b..c0208ba 100644
--- a/docs/i18n/de.md
+++ b/docs/i18n/de.md
@@ -6,26 +6,34 @@
# Internationalizer
-KI-native Internationalisierungs-Pipeline für Softwareprojekte. Übersetzen, validieren und verwalten Sie i18n-Dateien mithilfe von LLMs.
+KI-native Internationalisierungs-Pipeline für Softwareprojekte. Übersetze, validiere und verwalte i18n-Dateien mithilfe von LLMs.
[](https://github.com/Tom-R-Main/Internationalizer/actions/workflows/ci.yml)
-[](LICENSE)
+[](../../LICENSE)
+
+العربية · বাংলা · Čeština · Dansk · Deutsch · Ελληνικά · Español · Suomi · Français · עברית · हिन्दी · Indonesia · Italiano · 日本語 · 한국어 · Bahasa Melayu
Nederlands · ਪੰਜਾਬੀ · Polski · Português · Română · Русский · Svenska · తెలుగు · ไทย · Türkçe · Українська · Tiếng Việt · 粵語 · 简体中文 · 繁體中文
+
+
+---
+
+
## Warum Internationalizer?
-Die meisten i18n-Tools sind entweder Laufzeitbibliotheken (i18next, react-intl) oder SaaS-Plattformen zur Schlüsselverwaltung (Crowdin, Lokalise). Keines davon löst das eigentliche Übersetzungsproblem gut:
+Die meisten i18n-Tools sind entweder Laufzeitbibliotheken (i18next, react-intl) oder SaaS-Plattformen zur Schlüsselverwaltung (Crowdin, Lokalise). Keines davon löst das eigentliche Übersetzungsproblem zufriedenstellend:
-- **Manuelle Übersetzung** skaliert nicht über ein paar Sprachen hinaus
-- **Maschinelle Übersetzungs-APIs** (Google Translate, DeepL) ignorieren Ihre Terminologie, Ihren Tonfall und Ihre UI-Konventionen
-- **Generische LLM-Übersetzung** funktioniert besser, aber ohne Glossare und Styleguides erhalten Sie inkonsistente Ergebnisse
+- **Manuelle Übersetzung** skaliert über wenige Sprachen hinaus nicht mehr
+- **APIs für maschinelle Übersetzung** (Google Translate, DeepL) ignorieren deine Terminologie, deinen Tonfall und deine UI-Konventionen
+- **Generische LLM-Übersetzungen** funktionieren besser, führen aber ohne Glossare und Styleguides zu inkonsistenten Ergebnissen
-Internationalizer ist anders. Es ist eine **CLI-Pipeline**, die LLM-Übersetzung kombiniert mit:
+Internationalizer geht einen anderen Weg. Es ist eine **CLI-Pipeline**, die LLM-Übersetzung mit folgenden Bausteinen kombiniert:
-- **Sprachspezifischen Glossaren** — erzwingen eine konsistente Terminologie in Ihrer gesamten App
-- **Sprachspezifischen Styleguides** — steuern Tonfall, Formalität, Pluralbildung und Typografie
-- **Translation Memory** — überspringt unveränderte Zeichenfolgen und spart Kosten für API-Aufrufe
-- **Schlüsselvalidierung** — erkennt fehlende Übersetzungen und Interpolationsfehler vor der Auslieferung
+- **Sprachspezifische Glossare** – setzen eine konsistente Terminologie in deiner gesamten App durch
+- **Sprachspezifische Styleguides** – steuern Tonfall, Formalitätsgrad, Pluralisierung und Typografie
+- **Translation Memory** – überspringt unveränderte Strings und spart Kosten bei API-Aufrufen
+- **Deterministische Validierung** – fängt fehlende oder überflüssige Schlüssel, Abweichungen geschützter Strukturen, Glossarprobleme sowie Plural- oder ICU-Fehler ab, bevor sie ausgeliefert werden
+
## Installation
Über npm installieren:
@@ -56,22 +64,28 @@ cd Internationalizer
go build -o internationalizer ./cmd/internationalizer
```
+
## npm-Pakete
-- Git-Tags und npm-Paketversionen müssen übereinstimmen, zum Beispiel `v0.1.0` und `0.1.0`
-- Das Hauptpaket `internationalizer` hängt von Plattformpaketen wie `internationalizer-darwin-arm64` ab
-- Unterstützte npm-Ziele: macOS arm64/x64, Linux arm64/x64, Windows x64
-- Die CI-Veröffentlichung erfordert ein GitHub-Secret namens `NPM_TOKEN`
+- Git-Tags und npm-Paketversionen müssen übereinstimmen, beispielsweise `v0.1.0` und `0.1.0`
+- Das Root-Paket `internationalizer` hängt von Plattformpaketen wie `internationalizer-darwin-arm64` ab
+- Unterstützte npm-Zielplattformen: macOS arm64/x64, Linux arm64/x64, Windows x64
+- Das Veröffentlichen über CI erfordert ein GitHub-Secret namens `NPM_TOKEN`
+
## Schnellstart
-1. Erstellen Sie eine Konfigurationsdatei im Stammverzeichnis Ihres Projekts:
+1. Erstelle eine Konfigurationsdatei im Root-Verzeichnis deines Projekts:
```yaml
# .internationalizer.yml
source_locale: en
target_locales: [fr, de, es, ja]
-source_path: locales/en.json
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
llm:
provider: gemini
@@ -79,54 +93,83 @@ llm:
api_key_env: GOOGLE_AI_STUDIO_API_KEY
```
-2. Legen Sie Ihren API-Schlüssel fest:
+2. Setze deinen API-Schlüssel:
```bash
export GOOGLE_AI_STUDIO_API_KEY=your-ai-studio-key
```
-3. Vorschau der zu übersetzenden Inhalte anzeigen:
+3. Zeige eine Vorschau der anstehenden Übersetzungen an:
```bash
internationalizer translate --dry-run
```
-4. Übersetzung ausführen:
+4. Führe die Übersetzung aus:
```bash
internationalizer translate
```
-5. Alle Gebietsschemas validieren:
+5. Validiere alle Locales:
```bash
internationalizer validate
```
+
## Befehle
### `translate`
-Fehlende Schlüssel finden und über ein LLM übersetzen.
+Fehlende oder veraltete Schlüssel ermitteln und über ein LLM übersetzen.
```bash
-internationalizer translate # Alle Gebietsschemas übersetzen
-internationalizer translate -l fr # Nur Französisch übersetzen
-internationalizer translate --dry-run # Vorschau ohne API-Aufrufe
-internationalizer translate --batch-size 20 # Kleinere Batches
-internationalizer translate --concurrency 2 # Weniger parallele Aufrufe
+internationalizer translate # translate all locales
+internationalizer translate -l fr # translate French only
+internationalizer translate --dry-run # preview without API calls
+internationalizer translate --adopt-existing # baseline existing translations without API calls
+internationalizer translate --refresh-policy # refresh prompt/style/model-stale entries
+internationalizer translate --batch-size 20 # smaller batches
+internationalizer translate --concurrency 2 # fewer parallel calls
```
+Der Übersetzungsstatus meldet fehlende, quellseitig veraltete (source-stale), richtlinienseitig veraltete (policy-stale), aktuelle und manuell bearbeitete Zustände unabhängig voneinander, sodass eine manuelle Bearbeitung eine Quell- oder Richtlinienänderung nicht verdecken kann. Richtlinienseitig veraltete Werte werden gemeldet, aber nur mit `--refresh-policy` neu übersetzt. Manuell bearbeitete Werte werden niemals automatisch überschrieben. Verwende `--adopt-existing`, wenn du das Manifest für bereits geprüfte Übersetzungen einführst oder eine geprüfte manuelle Änderung ausdrücklich als neue Baseline übernimmst.
+
### `validate`
-Alle Gebietsschema-Dateien auf fehlende Schlüssel, zusätzliche Schlüssel und Interpolationsfehler prüfen.
+Alle Locale-Dateien mit ihren Quell-Bundles abgleichen. Die Standardvalidierung prüft die strukturelle Abdeckung (den Prozentsatz der vorhandenen erforderlichen Zielschlüssel), meldet überzählige Schlüssel als Warnungen und schlägt bei fehlenden Schlüsseln, Interpolationsabweichungen oder ungültiger ICU-MessageFormat-Struktur fehl.
```bash
-internationalizer validate # Menschenlesbare Ausgabe
-internationalizer validate --json # Maschinenlesbares JSON
-internationalizer validate -q # Nur Exit-Code
+internationalizer validate # human-readable output
+internationalizer validate --json # machine-readable JSON
+internationalizer validate -q # exit code only
+internationalizer validate --strict # enforce translation quality rules
+internationalizer validate --require-state # require current manifest provenance
```
+`--strict` gibt zusätzlich die übersetzte Abdeckung aus. Ein sprachlicher Wert, der mit seiner Quelle identisch ist, gilt als unübersetzt, es sei denn, das Glossar enthält ausdrücklich einen exakten Eintrag mit identischer Quelle und identischem Ziel für den vollständigen Wert; `ignore_case` wird beachtet, aber ein in einen längeren Wert eingebetteter Glossarbegriff gilt nicht als Ausnahme. Der Strict-Modus schlägt bei überzähligen Schlüsseln, quellidentischen Werten, veränderten Interpolations-, HTML-, Code- oder Markdown-Link-Strukturen, Glossarverstößen und konfigurierten Pluralformen fehl.
+
+`--require-state` gleicht jedes Ziel mit `.internationalizer.lock` ab. Die Prüfung schlägt fehl, wenn ein Schlüssel nicht erfasst ist (untracked) oder wenn der hinterlegte Hash für Quelle, Übersetzungsrichtlinie oder Ziel veraltet ist. Die Option kann mit `--strict` kombiniert werden.
+
+Ausgaben für Menschen und maschinenlesbare JSON-Berichte verwenden stabile Finding-Codes:
+
+| Code | Bedeutung |
+| --- | --- |
+| `missing_key` / `extra_key` | Quell- und Zielschlüsselsätze weichen voneinander ab |
+| `blank_translation` | Eine nicht leere Quelle hat im Strict-Modus ein leeres Ziel |
+| `source_identical` | Ein sprachlicher Wert bleibt im Strict-Modus unübersetzt |
+| `protected_structure_mismatch` | Struktur von Interpolationen, HTML, Code oder Links wurde verändert |
+| `glossary_violation` | Kein zulässiger Zielbegriff oder keine zulässige Variante gefunden |
+| `plural_form_missing` | Eine konfigurierte Pluralform des Locales fehlt |
+| `icu_message_syntax` | Eine ICU-Nachricht in der Quelle oder im Ziel ist syntaktisch fehlerhaft |
+| `icu_argument_mismatch` | ICU-Argumentnamen, -Typen oder -Formatierungsstile weichen ab |
+| `icu_selector_mismatch` | Selektoren weichen ab oder eine Pluralkategorie ist für das Ziel-Locale ungültig |
+| `untracked` | Für das Ziel existiert kein Eintrag im Manifest |
+| `source_stale` | Quellinhalt wurde nach der erfassten Übersetzung geändert |
+| `policy_stale` | Der generierte Prompt oder die Modelleinstellungen wurden geändert |
+| `target_modified` | Zielinhalt weicht vom Manifest-Eintrag ab |
+
### `detect`
Das i18n-Framework automatisch erkennen und eine Konfiguration vorschlagen.
@@ -135,11 +178,11 @@ Das i18n-Framework automatisch erkennen und eine Konfiguration vorschlagen.
internationalizer detect
```
-Unterstützt: react-i18next, next-intl, vue-i18n, Vanilla JSON, Markdown-Dokumente.
+Unterstützt: react-i18next, next-intl, vue-i18n, Standard-JSON, Markdown-Dokumentation.
### `glossary`
-Sprachspezifische Glossarbegriffe verwalten, die während der Übersetzung erzwungen werden.
+Sprachspezifische Glossarbegriffe verwalten, die während der Übersetzung durchgesetzt werden.
```bash
internationalizer glossary list --locale fr
@@ -149,85 +192,138 @@ internationalizer glossary remove --locale fr --source "Dashboard"
### `tm`
-Translation Memory verwalten (JSONL-Cache von zuvor übersetzten Zeichenfolgen).
+Das Translation Memory verwalten (JSONL-Cache zuvor übersetzter Strings).
```bash
-internationalizer tm stats # Datensatzanzahl anzeigen
-internationalizer tm export # Als JSON exportieren
-internationalizer tm clear --force # Alle Datensätze löschen
+internationalizer tm stats # show record counts
+internationalizer tm export # dump as JSON
+internationalizer tm clear --force # delete all records
```
+
## Konfigurationsreferenz
```yaml
# .internationalizer.yml
-# Quellsprache (Standard: en)
+# Source language (default: en)
source_locale: en
-# Zielsprachen für die Übersetzung (erforderlich)
-target_locales: [fr, de, es, ja, zh-CN, ar]
-
-# Pfad zur Quell-Gebietsschema-Datei (erforderlich)
-source_path: locales/en.json
-
-# LLM-Anbieter-Einstellungen
+# Languages to translate into (required)
+target_locales: [fr, de, es, ja, yue, zh-CN, zh-TW, ar]
+
+# One or more source-to-target mappings (required).
+# {locale} is replaced with each configured target locale.
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
+ - id: docs
+ source: README.md
+ target: docs/i18n/{locale}.md
+ format: markdown
+
+# Backward compatibility: source_path still maps targets to sibling files
+# such as locales/fr.json. Prefer bundles for new projects.
+# source_path: locales/en.json
+
+# LLM provider settings
llm:
- # Anbieter: "anthropic", "openai", "gemini" oder "openrouter" (Standard: gemini)
+ # Provider: "anthropic", "openai", "gemini", or "openrouter" (default: gemini)
provider: gemini
- # Standard-Modellnamen nach Anbieter:
+ # Model name defaults by provider:
# anthropic: claude-opus-5
- # openai: gpt-5.6-luna
+ # openai: gpt-5.6-luna (reasoning effort defaults to max)
# gemini: gemini-3.8-flash
# openrouter: deepseek/deepseek-v4-pro-0813
model: gemini-3.8-flash
- # Umgebungsvariable, die den API-Schlüssel enthält
+ # Environment variable containing the API key
api_key_env: GOOGLE_AI_STUDIO_API_KEY
- # Basis-URL für OpenAI-kompatible Endpunkte (optional)
+ # Base URL for OpenAI-compatible endpoints (optional)
# base_url: https://api.openai.com
-# Schlüssel pro LLM-Aufruf (Standard: 40)
+ # OpenAI GPT-5-series Responses API reasoning effort
+ # (default: max for the OpenAI provider)
+ reasoning_effort: max
+
+ # Optional LLM settings for individual target locales. An override using the
+ # global provider inherits unspecified global settings. A different provider
+ # uses that provider's defaults for unspecified settings.
+ locale_overrides:
+ yue:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-CN:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-TW:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+
+# Keys per LLM call (default: 40)
batch_size: 40
-# Parallele LLM-Aufrufe (Standard: 4)
+# Parallel LLM calls (default: 4)
concurrency: 4
-# Verzeichnis mit sprachspezifischen Styleguide-Markdown-Dateien (Standard: style-guides)
+# Directory containing per-locale style guide Markdown files (default: style-guides)
style_guides_dir: style-guides
-# Verzeichnis mit sprachspezifischen Glossar-JSON-Dateien (Standard: glossary)
+# Directory containing per-locale glossary JSON files (default: glossary)
glossary_dir: glossary
-# Pfad zur Translation-Memory-Datei (Standard: .internationalizer/tm.jsonl)
+# Path to translation memory file (default: .internationalizer/tm.jsonl)
tm_path: .internationalizer/tm.jsonl
+
+# Versioned source, policy, target, and provenance state
+# (default: .internationalizer.lock; commit this file)
+manifest_path: .internationalizer.lock
+
+# Optional translation and strict-validation rules
+validation:
+ plural_style: i18next-v4 # generate and validate target-locale plural forms
```
+Locale-IDs müssen wohlgeformte BCP-47-Tags wie `fr`, `pt-BR` oder `sr-Latn-RS` sein. Kanonisch äquivalente Ziel-Locales werden als Duplikate abgelehnt, und locale-spezifische Provider-Overrides berücksichtigen die kanonisch äquivalente Schreibweise. Im obigen Beispiel erben Locales ohne Override – einschließlich Japanisch – die globale Gemini-Konfiguration.
+
+Werte im ICU MessageFormat werden strukturell geparst. Einfache Argumente, `select`, `plural`, `selectordinal`, `number`, `date` und `time` werden unterstützt, einschließlich verschachtelter Nachrichten, Plural-Offsets, Selektoren für exakte Zahlen und `#`. Die Validierung prüft Syntax, Argumenttypen und Formatierungsstile, Plural-Offsets, Übereinstimmung der Select-Zweige sowie CLDR-Pluralkategorien des Ziel-Locales. Provider-Ausgaben, die diese Invarianten verletzen, werden verworfen, bevor eine Locale-Datei oder ein Datensatz im Translation Memory geschrieben wird.
+
+Mit `i18next-v4` werden erkannte Pluralfamilien der Quelle bei der Übersetzung in die CLDR-Kategorien des Ziel-Locales expandiert. Eine Kategorie, die nur im Ziel existiert, verwendet den `_other`-Wert der Quellfamilie als Übersetzungsvorlage. Die strikte Validierung verlangt diese Zielkategorien; reine Quellkategorien sind für Ziel-Locales optional, die diese nicht verwenden.
+
+
## Styleguides
-Styleguides sind Markdown-Dateien, die in den LLM-Übersetzungs-Prompt eingefügt werden. Sie steuern Tonfall, Formalität, Typografie und andere sprachspezifische Konventionen.
+Styleguides sind Markdown-Dateien, die in den LLM-Übersetzungs-Prompt eingefügt werden. Sie steuern Tonfall, Formalitätsgrad, Typografie und weitere sprachspezifische Konventionen.
```
style-guides/
- _conventions.md # Gemeinsame Regeln für alle Sprachen
- fr.md # Französisch-spezifische Regeln
- ja.md # Japanisch-spezifische Regeln
- ar.md # Arabisch-spezifische Regeln
+ _conventions.md # shared rules for all languages
+ fr.md # French-specific rules
+ ja.md # Japanese-specific rules
+ ar.md # Arabic-specific rules
```
### Gemeinsame Konventionen (`_conventions.md`)
-Definieren Sie Regeln, die für alle Sprachen gelten: Interpolationssyntax, Beibehaltung von HTML, Konventionen für Zeichenfolgentypen (Schaltflächen vs. Labels vs. Fehler) usw.
+Definiert Regeln, die für alle Sprachen gelten: Interpolationssyntax, Beibehaltung von HTML, Konventionen für String-Typen (Schaltflächen vs. Labels vs. Fehlermeldungen) usw.
+
+### Sprachspezifische Leitfäden (`{locale}.md`)
-### Sprachspezifische Guides (`{locale}.md`)
+Definiert sprachspezifische Regeln: Höflichkeitsformen (du vs. Sie), Zeichensetzung (Guillemets, umgekehrte Fragezeichen), Pluralformen, Datums- und Zahlenformate sowie ein Terminologieglossar.
-Definieren Sie sprachspezifische Regeln: Formalitätsregister (Du vs. Sie), Interpunktion (Guillemets, umgekehrte Fragezeichen), Pluralformen, Datums-/Zahlenformatierung und ein Terminologie-Glossar.
+Styleguides sind dauerhafte Richtlinien-Eingaben (Policy Inputs), keine generierten Ausgaben. Internationalizer liest sie ein, verändert sie jedoch niemals. Ihr Inhalt wird getrennt vom Glossar und dem Prompt-Contract gehasht, sodass Änderungen am Anwendungscode eine Übersetzung nicht als veraltet einstufen. Das Bearbeiten eines Guides markiert dieses Locale bewusst für eine Richtlinienüberprüfung; eine Änderung des internen Prompt-Wortlauts tut dies nicht, es sei denn, die Version des Prompt-Contracts ändert sich ebenfalls.
-Ein funktionierendes Beispiel finden Sie unter [`examples/react-app/style-guides/`](examples/react-app/style-guides/).
+Ein funktionierendes Beispiel findest du unter [`examples/react-app/style-guides/`](../../examples/react-app/style-guides/).
-## Glossar-Format
+
+## Glossarformat
Glossardateien sind JSON-Arrays, die in `{glossary_dir}/{locale}.json` gespeichert werden:
@@ -236,79 +332,95 @@ Glossardateien sind JSON-Arrays, die in `{glossary_dir}/{locale}.json` gespeiche
{
"source": "Dashboard",
"target": "Tableau de bord",
+ "variants": ["Panneau de contrôle"],
+ "enforcement": "error",
"ignore_case": false,
"whole_word": true
}
]
```
-Begriffe werden als Terminologietabelle in den LLM-Prompt eingefügt, um eine konsistente Übersetzung von Schlüsselbegriffen in Ihrer gesamten Anwendung sicherzustellen.
+`variants` listet weitere zulässige Zielformen auf. `enforcement` kann den Wert `error` oder `warning` annehmen oder für das standardmäßige Error-Verhalten weggelassen werden. Begriffe werden als Terminologietabelle in den LLM-Prompt übergeben, um eine konsistente Übersetzung in deiner gesamten Anwendung sicherzustellen. Ein exakter Eintrag wie `{"source":"API","target":"API"}` nimmt diesen vollständigen quellidentischen Wert zudem von strikten Befunden zu unübersetzten Werten aus; ein längerer Wert, der `API` lediglich enthält, wird dadurch jedoch nicht freigestellt.
+
## Translation Memory
Das Translation Memory wird als JSONL-Datei gespeichert (ein JSON-Datensatz pro Zeile). Jeder Datensatz enthält:
-- Den Quellschlüssel und -wert
-- Den übersetzten Wert
-- Einen SHA-256-Hash des Quellwerts
+- Das Bundle, den Schlüssel, den Quellwert, den übersetzten Wert und das kanonische Ziel-Locale
+- Hashes für Quelle, Styleguide, Glossar, Prompt-Contract und die kombinierte Richtlinie
+- Den Provider und das Modell, die die Übersetzung erzeugt haben
- Einen Zeitstempel
-Bei nachfolgenden Ausführungen werden unveränderte Zeichenfolgen aus dem TM-Cache bereitgestellt, ohne das LLM aufzurufen, was sowohl Zeit als auch API-Kosten spart. Die TM-Datei ist Git-freundlich und kann zusammen mit Ihren Gebietsschema-Dateien committet werden.
+Bei nachfolgenden Durchläufen werden Strings mit denselben Quell- und Richtlinien-Hashes direkt aus dem Cache bedient, ohne das LLM aufzurufen. Der Standardpfad liegt im ignorierten Verzeichnis `.internationalizer/`, bleibt also ein lokaler Cache. Setze `tm_path` auf einen versionierten Pfad, falls dein Projekt das Translation Memory gezielt teilen soll. Das prüfbare Manifest `.internationalizer.lock` wird separat versioniert.
+
## Unterstützte Formate
| Format | Erweiterungen | Modus |
-|--------|-----------|------|
-| JSON | `.json` | Schlüssel-Wert (verschachtelt, Punktnotation abgeflacht) |
+|--------|--------------|-------|
+| JSON | `.json` | Schlüssel-Wert (verschachtelt, mit Punktnotation verflacht) |
| YAML | `.yml`, `.yaml` | Schlüssel-Wert (behält Kommentare und Reihenfolge bei) |
-| Markdown | `.md`, `.mdx` | Übersetzung des gesamten Dokuments |
+| Markdown | `.md`, `.mdx` | Präambel und Abschnitte auf H2-Ebene |
+
+Markdown-Zieldateien enthalten unsichtbare `internationalizer:unit`-Kommentare vor H2-Abschnitten. Diese stabilen Markierungen ermöglichen es Internationalizer, einzelne Quellabschnitte hinzuzufügen, zu verschieben oder zu bearbeiten, ohne nicht betroffene Abschnitte erneut zu übersetzen. Bestehende Dokumente ohne Markierungen erhalten diese bei der nächsten erfolgreichen Aktualisierung.
+
## Erkennung des Projekttyps
-`internationalizer detect` identifiziert Ihr i18n-Setup durch Überprüfung von:
+`internationalizer detect` ermittelt dein i18n-Setup anhand folgender Kriterien:
-- `package.json`-Abhängigkeiten für react-i18next, next-intl oder vue-i18n
-- Verzeichnisstrukturen, die gängigen Gebietsschema-Mustern entsprechen
-- Dateierweiterungen und Namenskonventionen
+- Abhängigkeiten in `package.json` nach react-i18next, next-intl oder vue-i18n
+- Verzeichnisstrukturen, die gängigen Locale-Mustern entsprechen
+- Dateierweiterungen und Benennungskonventionen
+
## Architektur
```
-cmd/internationalizer/ CLI-Einstiegspunkt und Befehlsdefinitionen
+cmd/internationalizer/ CLI entry point and command definitions
internal/
- config/ Laden der YAML-Konfiguration mit Standardwerten
- detect/ Automatische Erkennung des Projekttyps
- formats/ Format-Parser (JSON, YAML, Markdown)
- glossary/ Sprachspezifische Glossarverwaltung
- llm/ LLM-Anbieter-Schnittstelle + Implementierungen
- anthropic.go Anthropic Claude-Backend
- openai.go OpenAI / kompatibles Backend
- gemini.go Google Gemini über AI Studio-Backend
- OpenRouter verwendet openai.go mit benutzerdefinierter base_url
- styleguide/ Styleguide-Lader
- tm/ JSONL Translation Memory
- translate/ Übersetzungs-Orchestrator
- validate/ Gebietsschema-Validierung und Diffing
+ config/ YAML config loading with defaults
+ detect/ Project type auto-detection
+ formats/ Format parsers (JSON, YAML, Markdown)
+ glossary/ Per-locale glossary management
+ llm/ LLM provider interface + implementations
+ anthropic.go Anthropic Claude backend
+ openai.go OpenAI / compatible backend
+ gemini.go Google Gemini via AI Studio backend
+ OpenRouter uses openai.go with custom base_url
+ locale/ BCP 47 identity and CLDR plural categories
+ message/ ICU MessageFormat parser and structural comparison
+ policy/ Stable translation-policy hashing
+ state/ Versioned translation manifest
+ styleguide/ Style guide loader
+ tm/ JSONL translation memory
+ translate/ Translation orchestrator
+ validate/ Locale validation and diffing
```
+
## Vergleich mit Alternativen
| Funktion | Internationalizer | i18next | Crowdin | Generisches LLM |
-|---------|------------------|---------|---------|-------------|
+|----------|-------------------|---------|---------|-----------------|
| LLM-gestützte Übersetzung | Ja | Nein | Teilweise | Ja |
| Sprachspezifische Styleguides | Ja | Nein | Nein | Nein |
-| Glossar-Erzwingung | Ja | Nein | Ja | Nein |
+| Glossardurchsetzung | Ja | Nein | Ja | Nein |
| Translation Memory | Ja | Nein | Ja | Nein |
-| CLI / lokale Ausführung | Ja | N/A | Nein | Manuell |
+| CLI- / lokale Ausführung | Ja | k. A. | Nein | Manuell |
| Git-freundliche Dateien | Ja | Ja | Teilweise | Manuell |
| Keine SaaS-Abhängigkeit | Ja | Ja | Nein | Variiert |
| Open Source (AGPL-3.0) | Ja | Ja | Nein | Variiert |
+
## Lizenz
-[AGPL-3.0](LICENSE)
+[AGPL-3.0](../../LICENSE)
-## Mitwirken
+Hinweise zu Drittanbieter-Lizenzen findest du in [THIRD_PARTY_NOTICES.md](../../THIRD_PARTY_NOTICES.md).
-Siehe [CONTRIBUTING.md](CONTRIBUTING.md) für Entwicklungs-Setup und Richtlinien. Alle Beiträge erfordern einen DCO-Sign-off.
+
+## Mitwirken
+Informationen zum Einrichten der Entwicklungsumgebung und Leitfäden findest du in [CONTRIBUTING.md](../../CONTRIBUTING.md). Alle Beiträge erfordern ein DCO-Sign-off.
diff --git a/docs/i18n/el.md b/docs/i18n/el.md
index b1abf00..31e5a43 100644
--- a/docs/i18n/el.md
+++ b/docs/i18n/el.md
@@ -6,42 +6,43 @@
# Internationalizer
-AI-native ροή εργασιών διεθνοποίησης (internationalization) για έργα λογισμικού. Μεταφράστε, επικυρώστε και διαχειριστείτε αρχεία i18n χρησιμοποιώντας LLMs.
+AI-native pipeline διεθνοποίησης για έργα λογισμικού. Μεταφράστε, επικυρώστε και διαχειριστείτε αρχεία i18n χρησιμοποιώντας LLMs.
[](https://github.com/Tom-R-Main/Internationalizer/actions/workflows/ci.yml)
-[](LICENSE)
+[](../../LICENSE)
-العربية · বাংলা · Deutsch · Español · Français · हिन्दी · Indonesia · 日本語 · ਪੰਜਾਬੀ · Português · Русский · తెలుగు
-ไทย · Українська · 粵語 · 简体中文 · 繁體中文
+العربية · বাংলা · Čeština · Dansk · Deutsch · Ελληνικά · Español · Suomi · Français · עברית · हिन्दी · Indonesia · Italiano · 日本語 · 한국어 · Bahasa Melayu
Nederlands · ਪੰਜਾਬੀ · Polski · Português · Română · Русский · Svenska · తెలుగు · ไทย · Türkçe · Українська · Tiếng Việt · 粵語 · 简体中文 · 繁體中文
---
+
## Γιατί το Internationalizer;
-Τα περισσότερα εργαλεία i18n είναι είτε βιβλιοθήκες χρόνου εκτέλεσης (i18next, react-intl) είτε πλατφόρμες SaaS διαχείρισης κλειδιών (Crowdin, Lokalise). Κανένα από αυτά δεν λύνει καλά το πραγματικό πρόβλημα της μετάφρασης:
+Τα περισσότερα εργαλεία i18n είναι είτε βιβλιοθήκες χρόνου εκτέλεσης (i18next, react-intl) είτε πλατφόρμες SaaS διαχείρισης κλειδιών (Crowdin, Lokalise). Κανένα από αυτά δεν επιλύει ικανοποιητικά το πραγματικό πρόβλημα της μετάφρασης:
-- Η **χειροκίνητη μετάφραση** δεν κλιμακώνεται πέρα από μερικές γλώσσες
-- Τα **API μηχανικής μετάφρασης** (Google Translate, DeepL) αγνοούν την ορολογία, το ύφος και τις συμβάσεις του περιβάλλοντος χρήστη (UI)
-- Η **γενική μετάφραση μέσω LLM** λειτουργεί καλύτερα, αλλά χωρίς γλωσσάρια και οδηγούς στυλ, έχετε ασυνεπή αποτελέσματα
+- Η **χειροκίνητη μετάφραση** δεν κλιμακώνεται πέρα από λίγες γλώσσες
+- Τα **API μηχανικής μετάφρασης** (Google Translate, DeepL) αγνοούν την ορολογία, το ύφος και τις συμβάσεις του UI σας
+- Η **γενική μετάφραση μέσω LLM** λειτουργεί καλύτερα, αλλά χωρίς γλωσσάρια και οδηγούς στυλ, τα αποτελέσματα είναι ασυνεπή
-Το Internationalizer είναι διαφορετικό. Είναι ένα **CLI pipeline** που συνδυάζει τη μετάφραση μέσω LLM με:
+Το Internationalizer διαφέρει. Είναι ένα **CLI pipeline** που συνδυάζει τη μετάφραση μέσω LLM με:
-- **Γλωσσάρια ανά γλώσσα** — επιβάλλουν συνεπή ορολογία σε όλη την εφαρμογή σας
-- **Οδηγούς στυλ ανά γλώσσα** — ελέγχουν το ύφος, την επισημότητα, τον πληθυντικό και την τυπογραφία
-- **Μεταφραστική μνήμη** — παραλείπει τις αμετάβλητες συμβολοσειρές, εξοικονομώντας χρήματα από κλήσεις API
-- **Επικύρωση κλειδιών** — εντοπίζει τις ελλιπείς μεταφράσεις και τις αναντιστοιχίες παρεμβολής (interpolation) πριν την κυκλοφορία
+- **Γλωσσάρια ανά γλώσσα** — επιβολή συνεκτικής ορολογίας σε ολόκληρη την εφαρμογή σας
+- **Οδηγούς στυλ ανά γλώσσα** — έλεγχος ύφους, βαθμού επισημότητας, σχηματισμού πληθυντικού και τυπογραφίας
+- **Μεταφραστική μνήμη** — παράλειψη αμετάβλητων συμβολοσειρών και εξοικονόμηση πόρων σε κλήσεις API
+- **Ντετερμινιστική επικύρωση** — εντοπισμός κλειδιών που λείπουν ή πλεονάζουν, απόκλισης προστατευμένης δομής, ζητημάτων γλωσσαρίου και σφαλμάτων πληθυντικού ή ICU πριν από τη διάθεση του κώδικα στην παραγωγή
+
## Εγκατάσταση
-Εγκατάσταση από το npm:
+Εγκαταστήστε από το npm:
```bash
npm install -g internationalizer
```
-Ή εκτέλεση χωρίς καθολική εγκατάσταση:
+Ή εκτελέστε χωρίς καθολική εγκατάσταση:
```bash
npx internationalizer --help
@@ -49,13 +50,13 @@ npx internationalizer --help
Το πακέτο npm εγκαθιστά το αντίστοιχο προμεταγλωττισμένο εκτελέσιμο από το npm μέσω προαιρετικών εξαρτήσεων για τη συγκεκριμένη πλατφόρμα.
-Εγκατάσταση με Go:
+Εγκαταστήστε με το Go:
```bash
go install github.com/Tom-R-Main/Internationalizer/cmd/internationalizer@latest
```
-Ή δημιουργία από τον πηγαίο κώδικα:
+Ή δημιουργήστε το εκτελέσιμο από τον πηγαίο κώδικα:
```bash
git clone https://github.com/Tom-R-Main/Internationalizer.git
@@ -63,13 +64,15 @@ cd Internationalizer
go build -o internationalizer ./cmd/internationalizer
```
+
## Πακέτα npm
-- Οι ετικέτες Git και οι εκδόσεις των πακέτων npm πρέπει να ταιριάζουν, για παράδειγμα `v0.1.0` και `0.1.0`
-- Το βασικό πακέτο `internationalizer` εξαρτάται από πακέτα πλατφόρμας όπως το `internationalizer-darwin-arm64`
+- Οι ετικέτες Git και οι εκδόσεις των πακέτων npm πρέπει να συμφωνούν απόλυτα, για παράδειγμα `v0.1.0` και `0.1.0`
+- Το ριζικό πακέτο `internationalizer` εξαρτάται από πακέτα πλατφόρμας όπως το `internationalizer-darwin-arm64`
- Υποστηριζόμενοι στόχοι npm: macOS arm64/x64, Linux arm64/x64, Windows x64
-- Η δημοσίευση μέσω CI απαιτεί ένα GitHub secret με το όνομα `NPM_TOKEN`
+- Η δημοσίευση μέσω CI απαιτεί ένα μυστικό GitHub με το όνομα `NPM_TOKEN`
+
## Γρήγορη εκκίνηση
1. Δημιουργήστε ένα αρχείο ρυθμίσεων στον ριζικό κατάλογο του έργου σας:
@@ -78,7 +81,11 @@ go build -o internationalizer ./cmd/internationalizer
# .internationalizer.yml
source_locale: en
target_locales: [fr, de, es, ja]
-source_path: locales/en.json
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
llm:
provider: gemini
@@ -92,7 +99,7 @@ llm:
export GOOGLE_AI_STUDIO_API_KEY=your-ai-studio-key
```
-3. Δείτε μια προεπισκόπηση του τι θα μεταφραστεί:
+3. Προεπισκοπήστε τι πρόκειται να μεταφραστεί:
```bash
internationalizer translate --dry-run
@@ -110,43 +117,72 @@ internationalizer translate
internationalizer validate
```
+
## Εντολές
### `translate`
-Βρείτε τα κλειδιά που λείπουν και μεταφράστε τα μέσω ενός LLM.
+Εντοπίστε κλειδιά που λείπουν ή είναι παρωχημένα και μεταφράστε τα μέσω ενός LLM.
```bash
internationalizer translate # translate all locales
internationalizer translate -l fr # translate French only
internationalizer translate --dry-run # preview without API calls
+internationalizer translate --adopt-existing # baseline existing translations without API calls
+internationalizer translate --refresh-policy # refresh prompt/style/model-stale entries
internationalizer translate --batch-size 20 # smaller batches
internationalizer translate --concurrency 2 # fewer parallel calls
```
+Η κατάσταση μετάφρασης αναφέρει ανεξάρτητα συνθήκες κλειδιών που λείπουν, παρωχημένης πηγής (source-stale), παρωχημένης πολιτικής (policy-stale), ενημερωμένων (current) και μη αυτόματα επεξεργασμένων (manually edited), ώστε μια χειροκίνητη τροποποίηση να μην μπορεί να αποκρύψει μια αλλαγή στην πηγή ή στην πολιτική. Οι τιμές με παρωχημένη πολιτική αναφέρονται, αλλά επαναμεταφράζονται μόνο με τη σημαία `--refresh-policy`. Οι τιμές που έχουν υποστεί χειροκίνητη επεξεργασία δεν αντικαθίστανται ποτέ αυτόματα. Χρησιμοποιήστε τη σημαία `--adopt-existing` κατά την εισαγωγή του manifest σε ελεγμένες μεταφράσεις ή όταν αποδέχεστε ρητά μια ελεγμένη μη αυτόματη τροποποίηση ως τη νέα γραμμή βάσης.
+
### `validate`
-Ελέγξτε όλα τα αρχεία locale για κλειδιά που λείπουν, πλεονάζοντα κλειδιά και αναντιστοιχίες παρεμβολής.
+Ελέγξτε όλα τα αρχεία locale σε σχέση με τα πηγαία bundle τους. Η προεπιλεγμένη επικύρωση ελέγχει τη δομική κάλυψη (το ποσοστό των απαιτούμενων κλειδιών προορισμού που υπάρχουν), αναφέρει τυχόν πλεονάζοντα κλειδιά ως προειδοποιήσεις και αποτυγχάνει όταν υπάρχουν κλειδιά που λείπουν, αναντιστοιχίες παρεμβολής ή μη έγκυρη δομή ICU MessageFormat.
```bash
internationalizer validate # human-readable output
internationalizer validate --json # machine-readable JSON
internationalizer validate -q # exit code only
+internationalizer validate --strict # enforce translation quality rules
+internationalizer validate --require-state # require current manifest provenance
```
+Η σημαία `--strict` αναφέρει επιπλέον τη μεταφρασμένη κάλυψη. Μια γλωσσική τιμή πανομοιότυπη με την πηγή της θεωρείται αμετάφραστη, εκτός εάν το γλωσσάρι περιέχει ρητά μια ακριβή εγγραφή με ίδια πηγή και ίδιο προορισμό για ολόκληρη την τιμή. Η ρύθμιση `ignore_case` τηρείται, αλλά ένας όρος γλωσσαρίου ενσωματωμένος μέσα σε μεγαλύτερη τιμή δεν αποτελεί εξαίρεση. Η αυστηρή λειτουργία (strict mode) αποτυγχάνει σε πλεονάζοντα κλειδιά, τιμές πανομοιότυπες με την πηγή, τροποποιημένη δομή παρεμβολής/HTML/κώδικα/συνδέσμων Markdown, παραβιάσεις γλωσσαρίου και μη έγκυρες ρυθμισμένες μορφές πληθυντικού.
+
+Η σημαία `--require-state` επαληθεύει κάθε αρχείο προορισμού έναντι του αρχείου `.internationalizer.lock`. Αποτυγχάνει όταν ένα κλειδί δεν παρακολουθείται (untracked) ή όταν η καταγεγραμμένη πηγή του, η πολιτική μετάφρασης ή το hash προορισμού είναι παρωχημένα. Μπορεί να συνδυαστεί με το `--strict`.
+
+Οι αναφορές για ανθρώπινη ανάγνωση και σε μορφή JSON χρησιμοποιούν σταθερούς κωδικούς ευρημάτων:
+
+| Κωδικός | Ερμηνεία |
+| --- | --- |
+| `missing_key` / `extra_key` | Τα σύνολα κλειδιών πηγής και προορισμού διαφέρουν |
+| `blank_translation` | Μια μη κενή πηγή έχει κενό προορισμό σε αυστηρή λειτουργία (strict mode) |
+| `source_identical` | Μια γλωσσική τιμή σε αυστηρή λειτουργία παραμένει αμετάφραστη |
+| `protected_structure_mismatch` | Η δομή παρεμβολής, HTML, κώδικα ή συνδέσμου έχει τροποποιηθεί |
+| `glossary_violation` | Δεν βρέθηκε εγκεκριμένος όρος προορισμού ή παραλλαγή |
+| `plural_form_missing` | Απουσιάζει μια ρυθμισμένη μορφή πληθυντικού για το locale |
+| `icu_message_syntax` | Ένα πηγαίο μήνυμα ή μήνυμα προορισμού ICU είναι κακοσχηματισμένο |
+| `icu_argument_mismatch` | Τα ονόματα, οι τύποι ή τα στυλ μορφοποιητή των ορισμάτων ICU διαφέρουν |
+| `icu_selector_mismatch` | Οι επιλογείς (selectors) διαφέρουν ή μια κατηγορία πληθυντικού δεν είναι έγκυρη για το locale προορισμού |
+| `untracked` | Δεν υπάρχει εγγραφή manifest για τον προορισμό |
+| `source_stale` | Το πηγαίο περιεχόμενο τροποποιήθηκε μετά την καταγεγραμμένη μετάφραση |
+| `policy_stale` | Το παραγόμενο prompt ή οι ρυθμίσεις μοντέλου άλλαξαν |
+| `target_modified` | Το περιεχόμενο προορισμού διαφέρει από την εγγραφή του manifest |
+
### `detect`
-Εντοπίστε αυτόματα το πλαίσιο i18n και λάβετε προτάσεις για τις ρυθμίσεις.
+Εντοπίστε αυτόματα το πλαίσιο i18n και λάβετε προτεινόμενες ρυθμίσεις παραμέτρων.
```bash
internationalizer detect
```
-Υποστηρίζει: react-i18next, next-intl, vue-i18n, vanilla JSON, markdown docs.
+Υποστηρίζει: react-i18next, next-intl, vue-i18n, vanilla JSON, έγγραφα markdown.
### `glossary`
-Διαχειριστείτε τους όρους του γλωσσαρίου ανά γλώσσα που επιβάλλονται κατά τη μετάφραση.
+Διαχειριστείτε όρους γλωσσαρίου ανά γλώσσα που επιβάλλονται υποχρεωτικά κατά τη μετάφραση.
```bash
internationalizer glossary list --locale fr
@@ -156,7 +192,7 @@ internationalizer glossary remove --locale fr --source "Dashboard"
### `tm`
-Διαχειριστείτε τη μεταφραστική μνήμη (προσωρινή μνήμη JSONL των ήδη μεταφρασμένων συμβολοσειρών).
+Διαχειριστείτε τη μεταφραστική μνήμη (προσωρινή μνήμη JSONL προηγουμένως μεταφρασμένων συμβολοσειρών).
```bash
internationalizer tm stats # show record counts
@@ -164,6 +200,7 @@ internationalizer tm export # dump as JSON
internationalizer tm clear --force # delete all records
```
+
## Αναφορά ρυθμίσεων
```yaml
@@ -173,10 +210,23 @@ internationalizer tm clear --force # delete all records
source_locale: en
# Languages to translate into (required)
-target_locales: [fr, de, es, ja, zh-CN, ar]
-
-# Path to the source locale file (required)
-source_path: locales/en.json
+target_locales: [fr, de, es, ja, yue, zh-CN, zh-TW, ar]
+
+# One or more source-to-target mappings (required).
+# {locale} is replaced with each configured target locale.
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
+ - id: docs
+ source: README.md
+ target: docs/i18n/{locale}.md
+ format: markdown
+
+# Backward compatibility: source_path still maps targets to sibling files
+# such as locales/fr.json. Prefer bundles for new projects.
+# source_path: locales/en.json
# LLM provider settings
llm:
@@ -185,7 +235,7 @@ llm:
# Model name defaults by provider:
# anthropic: claude-opus-5
- # openai: gpt-5.6-luna
+ # openai: gpt-5.6-luna (reasoning effort defaults to max)
# gemini: gemini-3.8-flash
# openrouter: deepseek/deepseek-v4-pro-0813
model: gemini-3.8-flash
@@ -196,6 +246,27 @@ llm:
# Base URL for OpenAI-compatible endpoints (optional)
# base_url: https://api.openai.com
+ # OpenAI GPT-5-series Responses API reasoning effort
+ # (default: max for the OpenAI provider)
+ reasoning_effort: max
+
+ # Optional LLM settings for individual target locales. An override using the
+ # global provider inherits unspecified global settings. A different provider
+ # uses that provider's defaults for unspecified settings.
+ locale_overrides:
+ yue:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-CN:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-TW:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+
# Keys per LLM call (default: 40)
batch_size: 40
@@ -210,11 +281,26 @@ glossary_dir: glossary
# Path to translation memory file (default: .internationalizer/tm.jsonl)
tm_path: .internationalizer/tm.jsonl
+
+# Versioned source, policy, target, and provenance state
+# (default: .internationalizer.lock; commit this file)
+manifest_path: .internationalizer.lock
+
+# Optional translation and strict-validation rules
+validation:
+ plural_style: i18next-v4 # generate and validate target-locale plural forms
```
+Τα αναγνωριστικά locale πρέπει να είναι καλοσχηματισμένες ετικέτες κατά BCP 47, όπως `fr`, `pt-BR` ή `sr-Latn-RS`. Τα κανονικά ισοδύναμα locale προορισμού απορρίπτονται ως διπλότυπα και οι ειδικές παρακάμψεις παρόχου ανά locale αντιστοιχίζονται με την κανονικά ισοδύναμη γραφή. Στο παραπάνω παράδειγμα, τα locale χωρίς παράκαμψη —συμπεριλαμβανομένων των Ιαπωνικών— κληρονομούν τις καθολικές ρυθμίσεις του Gemini.
+
+Οι τιμές ICU MessageFormat αναλύονται δομικά. Υποστηρίζονται απλά ορίσματα, `select`, `plural`, `selectordinal`, `number`, `date` και `time`, συμπεριλαμβανομένων ένθετων μηνυμάτων, μετατοπίσεων πληθυντικού (plural offsets), επιλογέων ακριβούς αριθμού και του συμβόλου `#`. Η επικύρωση ελέγχει τη σύνταξη, τους τύπους ορισμάτων και τα στυλ μορφοποιητή, τις μετατοπίσεις πληθυντικού, την ταυτότητα των κλάδων select και τις κατηγορίες πληθυντικού CLDR του locale προορισμού. Τα αποτελέσματα του παρόχου που παραβιάζουν αυτές τις αναλλοίωτες συνθήκες απορρίπτονται προτού εγγραφεί ένα αρχείο locale ή μια εγγραφή μεταφραστικής μνήμης.
+
+Με το `i18next-v4`, οι αναγνωρισμένες οικογένειες πληθυντικού της πηγής επεκτείνονται κατά τη μετάφραση στις κατηγορίες CLDR του locale προορισμού. Μια κατηγορία που υπάρχει μόνο στον προορισμό χρησιμοποιεί την τιμή `_other` της οικογένειας πηγής ως πρότυπο μετάφρασης. Η αυστηρή επικύρωση απαιτεί αυτές τις κατηγορίες προορισμού. Οι κατηγορίες που υπάρχουν μόνο στην πηγή είναι προαιρετικές για τα locale προορισμού που δεν τις χρησιμοποιούν.
+
+
## Οδηγοί στυλ
-Οι οδηγοί στυλ είναι αρχεία Markdown που εισάγονται στο prompt μετάφρασης του LLM. Ελέγχουν το ύφος, την επισημότητα, την τυπογραφία και άλλες συμβάσεις που αφορούν τη συγκεκριμένη γλώσσα.
+Οι οδηγοί στυλ είναι αρχεία Markdown που εισάγονται στο prompt μετάφρασης του LLM. Ελέγχουν το ύφος, την επισημότητα, την τυπογραφία και άλλες συμβάσεις ειδικές για κάθε γλώσσα.
```
style-guides/
@@ -230,54 +316,65 @@ style-guides/
### Οδηγοί ανά γλώσσα (`{locale}.md`)
-Ορίστε κανόνες για τη συγκεκριμένη γλώσσα: επίπεδο επισημότητας (π.χ. ενικός/πληθυντικός ευγενείας), στίξη (εισαγωγικά, ανεστραμμένα ερωτηματικά), μορφές πληθυντικού, μορφοποίηση ημερομηνίας/αριθμών και ένα γλωσσάρι ορολογίας.
+Ορίστε κανόνες για τη συγκεκριμένη γλώσσα: επίπεδο επισημότητας (ενικός έναντι πληθυντικού ευγενείας), στίξη (εισαγωγικά, ερωτηματικά), μορφές πληθυντικού, μορφοποίηση ημερομηνιών/αριθμών και ένα γλωσσάρι ορολογίας.
-Δείτε το [`examples/react-app/style-guides/`](examples/react-app/style-guides/) για ένα παράδειγμα σε λειτουργία.
+Οι οδηγοί στυλ αποτελούν ανθεκτικές εισόδους πολιτικής και όχι παραγόμενα αποτελέσματα. Το Internationalizer τους διαβάζει, αλλά δεν τους επανεγγράφει ποτέ. Το περιεχόμενό τους κατακερματίζεται (hashed) ξεχωριστά από το γλωσσάρι και το συμβόλαιο prompt, ώστε μια αλλαγή στον κώδικα της εφαρμογής να μην καθιστά μια μετάφραση παρωχημένη. Η επεξεργασία ενός οδηγού επισημαίνει σκόπιμα το συγκεκριμένο locale για επανέλεγχο πολιτικής. Αντίθετα, η αλλαγή της εσωτερικής διατύπωσης του prompt δεν προκαλεί επανέλεγχο, εκτός εάν αλλάξει και η έκδοση του συμβολαίου prompt.
+Δείτε το [`examples/react-app/style-guides/`](../../examples/react-app/style-guides/) για ένα λειτουργικό παράδειγμα.
+
+
## Μορφή γλωσσαρίου
-Τα αρχεία γλωσσαρίου είναι πίνακες JSON που αποθηκεύονται στο `{glossary_dir}/{locale}.json`:
+Τα αρχεία γλωσσαρίου είναι πίνακες JSON που αποθηκεύονται στη διαδρομή `{glossary_dir}/{locale}.json`:
```json
[
{
"source": "Dashboard",
"target": "Tableau de bord",
+ "variants": ["Panneau de contrôle"],
+ "enforcement": "error",
"ignore_case": false,
"whole_word": true
}
]
```
-Οι όροι εισάγονται στο prompt του LLM ως πίνακας ορολογίας, διασφαλίζοντας τη συνεπή μετάφραση βασικών όρων σε όλη την εφαρμογή σας.
+Το πεδίο `variants` παραθέτει άλλες εγκεκριμένες μορφές προορισμού. Το πεδίο `enforcement` μπορεί να οριστεί σε `error`, `warning` ή να παραλειφθεί για την προεπιλεγμένη συμπεριφορά σφάλματος. Οι όροι εισάγονται στο prompt του LLM ως πίνακας ορολογίας, διασφαλίζοντας συνεπή μετάφραση σε ολόκληρη την εφαρμογή σας. Μια ακριβής εγγραφή όπως το `{"source":"API","target":"API"}` εξαιρεί επίσης αυτή την πλήρη, ταυτόσημη με την πηγή τιμή από τα αυστηρά ευρήματα αμετάφραστων τιμών. Δεν εξαιρεί, ωστόσο, μια μεγαλύτερη τιμή που απλώς περιέχει τη λέξη `API`.
+
## Μεταφραστική μνήμη
Η μεταφραστική μνήμη αποθηκεύεται ως αρχείο JSONL (μία εγγραφή JSON ανά γραμμή). Κάθε εγγραφή περιέχει:
-- Το κλειδί και την τιμή προέλευσης
-- Τη μεταφρασμένη τιμή
-- Ένα κατακερματισμό (hash) SHA-256 της τιμής προέλευσης
+- Το bundle, το κλειδί, την τιμή πηγής, τη μεταφρασμένη τιμή και το κανονικό locale προορισμού
+- Τα hash πηγής, οδηγού στυλ, γλωσσαρίου, συμβολαίου prompt και συνδυασμένης πολιτικής
+- Τον πάροχο και το μοντέλο που παρήγαγαν τη μετάφραση
- Μια χρονική σήμανση (timestamp)
-Σε επόμενες εκτελέσεις, οι αμετάβλητες συμβολοσειρές εξυπηρετούνται από την προσωρινή μνήμη TM χωρίς κλήση στο LLM, εξοικονομώντας χρόνο και κόστος API. Το αρχείο TM είναι φιλικό προς το git και μπορεί να υποβληθεί (commit) μαζί με τα αρχεία locale σας.
+Σε επόμενες εκτελέσεις, οι συμβολοσειρές με τα ίδια hash πηγής και πολιτικής εξυπηρετούνται απευθείας από την προσωρινή μνήμη χωρίς κλήση στο LLM. Η προεπιλεγμένη διαδρομή βρίσκεται στον αγνοούμενο κατάλογο `.internationalizer/`, ώστε να παραμένει τοπική μνήμη cache. Ορίστε το `tm_path` σε μια παρακολουθούμενη τοποθεσία εάν το έργο σας μοιράζεται σκόπιμα τη μεταφραστική μνήμη. Το αναθεωρήσιμο manifest `.internationalizer.lock` παρακολουθείται σε ξεχωριστή έκδοση.
+
## Υποστηριζόμενες μορφές
| Μορφή | Επεκτάσεις | Λειτουργία |
-|--------|-----------|------|
+|---|---|---|
| JSON | `.json` | Key-value (ένθετο, επίπεδο με dot-notation) |
| YAML | `.yml`, `.yaml` | Key-value (διατηρεί σχόλια και σειρά) |
-| Markdown | `.md`, `.mdx` | Μετάφραση ολόκληρου εγγράφου |
+| Markdown | `.md`, `.mdx` | Προοίμιο και ενότητες επιπέδου H2 |
+
+Οι προορισμοί Markdown περιέχουν αόρατα σχόλια `internationalizer:unit` πριν από τις ενότητες H2. Αυτοί οι σταθεροί δείκτες επιτρέπουν στο Internationalizer να προσθέτει, να μετακινεί ή να επεξεργάζεται μία πηγαία ενότητα χωρίς να επαναμεταφράζει μη σχετικές ενότητες. Τα υπάρχοντα έγγραφα χωρίς σήμανση λαμβάνουν δείκτες κατά την επόμενη επιτυχή ενημέρωσή τους.
+
## Εντοπισμός τύπου έργου
-Το `internationalizer detect` αναγνωρίζει τις ρυθμίσεις i18n σας ελέγχοντας:
+Η εντολή `internationalizer detect` αναγνωρίζει τις ρυθμίσεις i18n σας ελέγχοντας:
- Τις εξαρτήσεις του `package.json` για react-i18next, next-intl ή vue-i18n
-- Τις δομές καταλόγων που ταιριάζουν με κοινά μοτίβα locale
+- Τις δομές καταλόγων που αντιστοιχούν σε κοινά μοτίβα locale
- Τις επεκτάσεις αρχείων και τις συμβάσεις ονομασίας
+
## Αρχιτεκτονική
```
@@ -292,16 +389,21 @@ internal/
openai.go OpenAI / compatible backend
gemini.go Google Gemini via AI Studio backend
OpenRouter uses openai.go with custom base_url
+ locale/ BCP 47 identity and CLDR plural categories
+ message/ ICU MessageFormat parser and structural comparison
+ policy/ Stable translation-policy hashing
+ state/ Versioned translation manifest
styleguide/ Style guide loader
tm/ JSONL translation memory
translate/ Translation orchestrator
validate/ Locale validation and diffing
```
+
## Σύγκριση με εναλλακτικές
| Δυνατότητα | Internationalizer | i18next | Crowdin | Γενικό LLM |
-|---------|------------------|---------|---------|-------------|
+|---|---|---|---|---|
| Μετάφραση μέσω LLM | Ναι | Όχι | Μερικώς | Ναι |
| Οδηγοί στυλ ανά γλώσσα | Ναι | Όχι | Όχι | Όχι |
| Επιβολή γλωσσαρίου | Ναι | Όχι | Ναι | Όχι |
@@ -311,11 +413,14 @@ internal/
| Χωρίς εξάρτηση από SaaS | Ναι | Ναι | Όχι | Διαφέρει |
| Ανοιχτού κώδικα (AGPL-3.0) | Ναι | Ναι | Όχι | Διαφέρει |
+
## Άδεια χρήσης
-[AGPL-3.0](LICENSE)
+[AGPL-3.0](../../LICENSE)
-## Συνεισφορά
+Δείτε το αρχείο [THIRD_PARTY_NOTICES.md](../../THIRD_PARTY_NOTICES.md) για ειδοποιήσεις σχετικά με εξαρτήσεις τρίτων.
-Δείτε το [CONTRIBUTING.md](CONTRIBUTING.md) για τις ρυθμίσεις ανάπτυξης και τις οδηγίες. Όλες οι συνεισφορές απαιτούν υπογραφή DCO.
+
+## Συνεισφορά
+Δείτε το αρχείο [CONTRIBUTING.md](../../CONTRIBUTING.md) για τις ρυθμίσεις περιβάλλοντος ανάπτυξης και τις σχετικές οδηγίες. Όλες οι συνεισφορές απαιτούν υπογραφή DCO.
diff --git a/docs/i18n/es.md b/docs/i18n/es.md
index 543f384..69c31dd 100644
--- a/docs/i18n/es.md
+++ b/docs/i18n/es.md
@@ -6,72 +6,82 @@
# Internationalizer
-Pipeline de internacionalización nativo de IA para proyectos de software. Traduce, valida y gestiona archivos i18n usando LLMs.
+Pipeline de internacionalización nativo para IA en proyectos de software. Traduzca, valide y gestione archivos i18n mediante LLM.
[](https://github.com/Tom-R-Main/Internationalizer/actions/workflows/ci.yml)
-[](LICENSE)
+[](../../LICENSE)
+
+العربية · বাংলা · Čeština · Dansk · Deutsch · Ελληνικά · Español · Suomi · Français · עברית · हिन्दी · Indonesia · Italiano · 日本語 · 한국어 · Bahasa Melayu
Nederlands · ਪੰਜਾਬੀ · Polski · Português · Română · Русский · Svenska · తెలుగు · ไทย · Türkçe · Українська · Tiếng Việt · 粵語 · 简体中文 · 繁體中文
+
+
+---
+
## ¿Por qué Internationalizer?
-La mayoría de las herramientas i18n son bibliotecas de tiempo de ejecución (i18next, react-intl) o plataformas SaaS de gestión de claves (Crowdin, Lokalise). Ninguna de ellas resuelve bien el problema real de la traducción:
+La mayoría de las herramientas de i18n son bibliotecas de entorno de ejecución (i18next, react-intl) o plataformas SaaS de gestión de claves (Crowdin, Lokalise). Ninguna resuelve adecuadamente el problema real de la traducción:
- **La traducción manual** no escala más allá de unos pocos idiomas
-- **Las API de traducción automática** (Google Translate, DeepL) ignoran tu terminología, tono y convenciones de la interfaz de usuario
-- **La traducción genérica con LLM** funciona mejor, pero sin glosarios ni guías de estilo, obtienes resultados inconsistentes
+- **Las API de traducción automática** (Google Translate, DeepL) ignoran la terminología, el tono y las convenciones de la interfaz de usuario de su proyecto
+- **La traducción genérica con LLM** funciona mejor, pero sin glosarios ni guías de estilo produce resultados incoherentes
-Internationalizer es diferente. Es un **pipeline CLI** que combina la traducción con LLM con:
+Internationalizer es diferente. Es una **canalización por línea de comandos (CLI)** que combina la traducción mediante LLM con:
-- **Glosarios por idioma**: aplican una terminología coherente en toda tu aplicación
+- **Glosarios por idioma**: garantizan una terminología coherente en toda la aplicación
- **Guías de estilo por idioma**: controlan el tono, la formalidad, la pluralización y la tipografía
-- **Memoria de traducción**: omite las cadenas sin cambios, ahorrando dinero en llamadas a la API
-- **Validación de claves**: detecta traducciones faltantes y desajustes de interpolación antes de su publicación
-
+- **Memoria de traducción**: omite cadenas sin cambios para ahorrar costes en llamadas a la API
+- **Validación determinista**: detecta claves faltantes o sobrantes, desviaciones en estructuras protegidas, problemas de glosario y errores de plurales o de sintaxis ICU antes de pasar a producción
+
## Instalación
-Instalar desde npm:
+Instale el paquete desde npm:
```bash
npm install -g internationalizer
```
-O ejecutar sin una instalación global:
+O ejecútelo directamente sin instalación global:
```bash
npx internationalizer --help
```
-El paquete npm instala el binario precompilado correspondiente desde npm a través de dependencias opcionales específicas de la plataforma.
+El paquete npm instala el binario precompilado correspondiente desde npm mediante dependencias opcionales específicas para cada plataforma.
-Instalar con Go:
+Instalación con Go:
```bash
go install github.com/Tom-R-Main/Internationalizer/cmd/internationalizer@latest
```
-O compilar desde el código fuente:
+O compile directamente desde el código fuente:
```bash
git clone https://github.com/Tom-R-Main/Internationalizer.git
cd Internationalizer
go build -o internationalizer ./cmd/internationalizer
```
-
+
## Paquetes npm
-- Las etiquetas de Git y las versiones de los paquetes npm deben coincidir, por ejemplo `v0.1.0` y `0.1.0`
-- El paquete raíz `internationalizer` depende de paquetes de plataforma como `internationalizer-darwin-arm64`
-- Objetivos npm compatibles: macOS arm64/x64, Linux arm64/x64, Windows x64
-- La publicación en CI requiere un secreto de GitHub llamado `NPM_TOKEN`
-
+- Las etiquetas de Git y las versiones de los paquetes npm deben coincidir exactamente; por ejemplo, `v0.1.0` y `0.1.0`
+- El paquete raíz `internationalizer` depende de paquetes específicos de plataforma como `internationalizer-darwin-arm64`
+- Destinos de npm compatibles: macOS arm64/x64, Linux arm64/x64 y Windows x64
+- La publicación en CI requiere un secreto de GitHub denominado `NPM_TOKEN`
+
## Inicio rápido
-1. Crea un archivo de configuración en la raíz de tu proyecto:
+1. Cree un archivo de configuración en la raíz de su proyecto:
```yaml
# .internationalizer.yml
source_locale: en
target_locales: [fr, de, es, ja]
-source_path: locales/en.json
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
llm:
provider: gemini
@@ -79,67 +89,95 @@ llm:
api_key_env: GOOGLE_AI_STUDIO_API_KEY
```
-2. Configura tu clave API:
+2. Configure su clave de API:
```bash
export GOOGLE_AI_STUDIO_API_KEY=your-ai-studio-key
```
-3. Previsualiza lo que se traducirá:
+3. Previsualice los elementos que se traducirán:
```bash
internationalizer translate --dry-run
```
-4. Ejecuta la traducción:
+4. Ejecute la traducción:
```bash
internationalizer translate
```
-5. Valida todos los locales:
+5. Valide todas las configuraciones regionales:
```bash
internationalizer validate
```
-
+
## Comandos
### `translate`
-Encuentra claves faltantes y tradúcelas a través de un LLM.
+Detecta claves que faltan o están desactualizadas y las traduce mediante un LLM.
```bash
-internationalizer translate # traduce todos los locales
-internationalizer translate -l fr # traduce solo al francés
-internationalizer translate --dry-run # previsualiza sin llamadas a la API
-internationalizer translate --batch-size 20 # lotes más pequeños
-internationalizer translate --concurrency 2 # menos llamadas en paralelo
+internationalizer translate # translate all locales
+internationalizer translate -l fr # translate French only
+internationalizer translate --dry-run # preview without API calls
+internationalizer translate --adopt-existing # baseline existing translations without API calls
+internationalizer translate --refresh-policy # refresh prompt/style/model-stale entries
+internationalizer translate --batch-size 20 # smaller batches
+internationalizer translate --concurrency 2 # fewer parallel calls
```
+El estado de la traducción notifica de forma independiente las condiciones de ausencia, origen desactualizado, directiva desactualizada, estado al día y ediciones manuales, de modo que una edición manual nunca oculte un cambio de origen o de directiva. Los valores con directiva desactualizada se notifican, pero solo se vuelven a traducir con `--refresh-policy`. Los valores modificados manualmente nunca se sobrescriben de forma automática. Use `--adopt-existing` al incorporar el manifiesto a traducciones ya revisadas o al aceptar explícitamente una edición manual revisada como nueva base de referencia.
+
### `validate`
-Comprueba todos los archivos de locales en busca de claves faltantes, claves adicionales y desajustes de interpolación.
+Compara todos los archivos de configuración regional con sus paquetes de origen. La validación predeterminada comprueba la cobertura estructural (el porcentaje de claves de destino requeridas que están presentes), informa sobre claves sobrantes en forma de advertencias y genera un error si faltan claves, si hay discrepancias de interpolación o si la estructura ICU MessageFormat no es válida.
```bash
-internationalizer validate # salida legible para humanos
-internationalizer validate --json # JSON legible por máquina
-internationalizer validate -q # solo código de salida
+internationalizer validate # human-readable output
+internationalizer validate --json # machine-readable JSON
+internationalizer validate -q # exit code only
+internationalizer validate --strict # enforce translation quality rules
+internationalizer validate --require-state # require current manifest provenance
```
+`--strict` también informa sobre la cobertura traducida. Un valor lingüístico idéntico a su origen se considera no traducido a menos que el glosario contenga de forma explícita una entrada idéntica con el mismo origen y el mismo destino para el valor completo; se respeta `ignore_case`, pero un término de glosario insertado dentro de un valor más largo no constituye una excepción. El modo estricto falla ante claves sobrantes, valores idénticos al origen, alteraciones en la estructura de interpolación, HTML, código o vínculos Markdown, infracciones de glosario y formas plurales configuradas.
+
+`--require-state` comprueba cada destino frente a `.internationalizer.lock`. Genera un error si una clave no está registrada o si su origen registrado, directiva de traducción o hash de destino están desactualizados. Se puede combinar con `--strict`.
+
+Los informes legibles por humanos y en JSON utilizan códigos de resultado estables:
+
+| Código | Significado |
+| --- | --- |
+| `missing_key` / `extra_key` | Los conjuntos de claves de origen y destino difieren |
+| `blank_translation` | Un origen no vacío tiene un destino vacío en modo estricto |
+| `source_identical` | Un valor lingüístico en modo estricto permanece sin traducir |
+| `protected_structure_mismatch` | La estructura de interpolación, HTML, código o vínculo ha cambiado |
+| `glossary_violation` | No se encontró ningún término o variante aprobado en el destino |
+| `plural_form_missing` | Falta una forma plural configurada para el idioma de destino |
+| `icu_message_syntax` | Un mensaje ICU de origen o de destino tiene un formato incorrecto |
+| `icu_argument_mismatch` | Los nombres, tipos o estilos de formato de los argumentos ICU difieren |
+| `icu_selector_mismatch` | Los selectores difieren o una categoría plural no es válida para la configuración regional de destino |
+| `untracked` | No existe ningún registro en el manifiesto para el destino |
+| `source_stale` | El contenido de origen cambió después de registrarse la traducción |
+| `policy_stale` | La petición (prompt) generada o la configuración del modelo han cambiado |
+| `target_modified` | El contenido de destino difiere del registro del manifiesto |
+
### `detect`
-Detecta automáticamente el framework i18n y sugiere una configuración.
+Detecta automáticamente el marco de i18n utilizado y sugiere una configuración.
```bash
internationalizer detect
```
-Soporta: react-i18next, next-intl, vue-i18n, JSON puro, documentos markdown.
+Soporta: react-i18next, next-intl, vue-i18n, JSON sin dependencias (vanilla JSON), documentos Markdown.
### `glossary`
-Gestiona los términos del glosario por idioma que se aplican durante la traducción.
+Gestiona términos de glosario por idioma cuyo cumplimiento se exige durante la traducción.
```bash
internationalizer glossary list --locale fr
@@ -152,163 +190,222 @@ internationalizer glossary remove --locale fr --source "Dashboard"
Gestiona la memoria de traducción (caché JSONL de cadenas traducidas previamente).
```bash
-internationalizer tm stats # muestra el recuento de registros
-internationalizer tm export # vuelca como JSON
-internationalizer tm clear --force # elimina todos los registros
+internationalizer tm stats # show record counts
+internationalizer tm export # dump as JSON
+internationalizer tm clear --force # delete all records
```
-
+
## Referencia de configuración
```yaml
# .internationalizer.yml
-# Idioma de origen (por defecto: en)
+# Source language (default: en)
source_locale: en
-# Idiomas a los que traducir (requerido)
-target_locales: [fr, de, es, ja, zh-CN, ar]
-
-# Ruta al archivo de locale de origen (requerido)
-source_path: locales/en.json
-
-# Configuración del proveedor de LLM
+# Languages to translate into (required)
+target_locales: [fr, de, es, ja, yue, zh-CN, zh-TW, ar]
+
+# One or more source-to-target mappings (required).
+# {locale} is replaced with each configured target locale.
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
+ - id: docs
+ source: README.md
+ target: docs/i18n/{locale}.md
+ format: markdown
+
+# Backward compatibility: source_path still maps targets to sibling files
+# such as locales/fr.json. Prefer bundles for new projects.
+# source_path: locales/en.json
+
+# LLM provider settings
llm:
- # Proveedor: "anthropic", "openai", "gemini" u "openrouter" (por defecto: gemini)
+ # Provider: "anthropic", "openai", "gemini", or "openrouter" (default: gemini)
provider: gemini
- # Nombres de modelo por defecto según el proveedor:
+ # Model name defaults by provider:
# anthropic: claude-opus-5
- # openai: gpt-5.6-luna
+ # openai: gpt-5.6-luna (reasoning effort defaults to max)
# gemini: gemini-3.8-flash
# openrouter: deepseek/deepseek-v4-pro-0813
model: gemini-3.8-flash
- # Variable de entorno que contiene la clave API
+ # Environment variable containing the API key
api_key_env: GOOGLE_AI_STUDIO_API_KEY
- # URL base para endpoints compatibles con OpenAI (opcional)
+ # Base URL for OpenAI-compatible endpoints (optional)
# base_url: https://api.openai.com
-# Claves por llamada al LLM (por defecto: 40)
+ # OpenAI GPT-5-series Responses API reasoning effort
+ # (default: max for the OpenAI provider)
+ reasoning_effort: max
+
+ # Optional LLM settings for individual target locales. An override using the
+ # global provider inherits unspecified global settings. A different provider
+ # uses that provider's defaults for unspecified settings.
+ locale_overrides:
+ yue:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-CN:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-TW:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+
+# Keys per LLM call (default: 40)
batch_size: 40
-# Llamadas paralelas al LLM (por defecto: 4)
+# Parallel LLM calls (default: 4)
concurrency: 4
-# Directorio que contiene los archivos Markdown de la guía de estilo por locale (por defecto: style-guides)
+# Directory containing per-locale style guide Markdown files (default: style-guides)
style_guides_dir: style-guides
-# Directorio que contiene los archivos JSON del glosario por locale (por defecto: glossary)
+# Directory containing per-locale glossary JSON files (default: glossary)
glossary_dir: glossary
-# Ruta al archivo de memoria de traducción (por defecto: .internationalizer/tm.jsonl)
+# Path to translation memory file (default: .internationalizer/tm.jsonl)
tm_path: .internationalizer/tm.jsonl
+
+# Versioned source, policy, target, and provenance state
+# (default: .internationalizer.lock; commit this file)
+manifest_path: .internationalizer.lock
+
+# Optional translation and strict-validation rules
+validation:
+ plural_style: i18next-v4 # generate and validate target-locale plural forms
```
+Los identificadores de configuración regional deben ser etiquetas BCP 47 con formato correcto, como `fr`, `pt-BR` o `sr-Latn-RS`. Las configuraciones regionales de destino canónicamente equivalentes se rechazan por duplicadas, y las invalidaciones de proveedor específicas por configuración regional coinciden con la grafía canónicamente equivalente. En el ejemplo anterior, las configuraciones regionales sin una invalidación (incluido el japonés) heredan la configuración global de Gemini.
+
+Los valores de ICU MessageFormat se analizan estructuralmente. Se admiten argumentos simples, `select`, `plural`, `selectordinal`, `number`, `date` y `time`, incluidos mensajes anidados, desplazamientos de plural (offsets), selectores de números exactos y `#`. La validación comprueba la sintaxis, los tipos de argumentos y estilos de formateo, los desplazamientos de plural, la identidad de las ramas de selección y las categorías de plurales de CLDR para el idioma de destino. Las respuestas del proveedor que vulneren estas invariantes se rechazan antes de escribir cualquier archivo de configuración regional o registro en la memoria de traducción.
+
+Con `i18next-v4`, las familias de plurales reconocidas en el origen se expanden durante la traducción hacia las categorías CLDR correspondientes a la configuración regional de destino. Las categorías exclusivas del destino toman el valor `_other` de la familia de origen como plantilla de traducción. La validación estricta exige la presencia de esas categorías de destino; las categorías exclusivas del origen son opcionales para las configuraciones regionales de destino que no las empleen.
+
## Guías de estilo
-Las guías de estilo son archivos Markdown que se inyectan en el prompt de traducción del LLM. Controlan el tono, la formalidad, la tipografía y otras convenciones específicas del idioma.
+Las guías de estilo son archivos Markdown que se inyectan en la petición (prompt) de traducción del LLM. Controlan el tono, la formalidad, la tipografía y otras convenciones específicas de cada idioma.
```
style-guides/
- _conventions.md # reglas compartidas para todos los idiomas
- fr.md # reglas específicas para francés
- ja.md # reglas específicas para japonés
- ar.md # reglas específicas para árabe
+ _conventions.md # shared rules for all languages
+ fr.md # French-specific rules
+ ja.md # Japanese-specific rules
+ ar.md # Arabic-specific rules
```
### Convenciones compartidas (`_conventions.md`)
-Define reglas que se aplican a todos los idiomas: sintaxis de interpolación, preservación de HTML, convenciones de tipos de cadenas (botones vs. etiquetas vs. errores), etc.
+Define las reglas aplicables a todos los idiomas: sintaxis de interpolación, preservación de HTML, convenciones de tipos de cadenas (botones frente a etiquetas o errores), etc.
### Guías por idioma (`{locale}.md`)
-Define reglas específicas del idioma: registro de formalidad (tú vs. usted), puntuación (comillas angulares, signos de interrogación invertidos), formas plurales, formato de fechas/números y un glosario terminológico.
+Define las reglas específicas para cada idioma: registro de formalidad (tú frente a usted), puntuación (comillas angulares, signos de apertura de interrogación), formas plurales, formatos de fecha y números, y un glosario terminológico.
-Consulta [`examples/react-app/style-guides/`](examples/react-app/style-guides/) para ver un ejemplo funcional.
+Las guías de estilo constituyen entradas normativas y duraderas, no resultados generados. Internationalizer las lee, pero nunca las sobrescribe. Su contenido se procesa mediante un hash independiente del glosario y del contrato de petición, por lo que una modificación en el código de la aplicación no marca una traducción como desactualizada. La modificación deliberada de una guía marca ese idioma para revisión de directiva; los cambios en la redacción interna de la petición no lo hacen, a menos que también varíe la versión del contrato de petición.
-## Formato del glosario
+Consulte [`examples/react-app/style-guides/`](../../examples/react-app/style-guides/) para ver un ejemplo funcional.
+
+## Formato de glosario
-Los archivos de glosario son arrays JSON almacenados en `{glossary_dir}/{locale}.json`:
+Los archivos de glosario son matrices JSON almacenadas en `{glossary_dir}/{locale}.json`:
```json
[
{
"source": "Dashboard",
"target": "Tableau de bord",
+ "variants": ["Panneau de contrôle"],
+ "enforcement": "error",
"ignore_case": false,
"whole_word": true
}
]
```
-Los términos se inyectan en el prompt del LLM como una tabla terminológica, asegurando una traducción coherente de los términos clave en toda tu aplicación.
-
+`variants` enumera otras formas de destino autorizadas. `enforcement` puede establecerse en `error`, `warning` o bien omitirse para conservar el comportamiento predeterminado de error. Los términos se incorporan al prompt del LLM en forma de tabla de terminología, lo que garantiza una traducción coherente en toda la aplicación. Una entrada exacta como `{"source":"API","target":"API"}` también exime a ese valor completo idéntico al origen de ser marcado como no traducido en el modo estricto; sin embargo, no exime a un valor más extenso que simplemente contenga `API`.
+
## Memoria de traducción
-La memoria de traducción se almacena como un archivo JSONL (un registro JSON por línea). Cada registro contiene:
+La memoria de traducción se almacena en un archivo JSONL (un registro JSON por línea). Cada registro contiene:
-- La clave y el valor de origen
-- El valor traducido
-- Un hash SHA-256 del valor de origen
+- El paquete, la clave, el valor de origen, el valor traducido y la configuración regional de destino canónica
+- Los valores hash del origen, la guía de estilo, el glosario, el contrato de la petición y la directiva combinada
+- El proveedor y el modelo que produjeron la traducción
- Una marca de tiempo
-En ejecuciones posteriores, las cadenas sin cambios se sirven desde la caché de la memoria de traducción (TM) sin llamar al LLM, ahorrando tiempo y costes de API. El archivo TM es compatible con git y se puede confirmar junto con tus archivos de locales.
-
-## Formatos soportados
+En las ejecuciones posteriores, las cadenas con los mismos hashes de origen y de directiva se recuperan de la memoria caché sin realizar llamadas al LLM. La ruta predeterminada se sitúa bajo el directorio omitido `.internationalizer/`, por lo que se mantiene como una caché local. Asigne a `tm_path` una ruta bajo control de versiones si en su proyecto se decide compartir deliberadamente la memoria de traducción. El manifiesto auditable `.internationalizer.lock` se gestiona por versiones de forma independiente.
+
+## Formatos admitidos
| Formato | Extensiones | Modo |
-|--------|-----------|------|
+| --- | --- | --- |
| JSON | `.json` | Clave-valor (anidado, aplanado con notación de puntos) |
-| YAML | `.yml`, `.yaml` | Clave-valor (preserva comentarios y orden) |
-| Markdown | `.md`, `.mdx` | Traducción de documento completo |
+| YAML | `.yml`, `.yaml` | Clave-valor (conserva comentarios y ordenación) |
+| Markdown | `.md`, `.mdx` | Preámbulo y secciones de nivel H2 |
+Los archivos de destino Markdown incluyen comentarios invisibles `internationalizer:unit` antes de las secciones H2. Estos marcadores estables permiten a Internationalizer añadir, mover o editar una sección de origen sin volver a traducir secciones no relacionadas. Los documentos existentes que carezcan de marcas las recibirán en su siguiente actualización correcta.
+
## Detección del tipo de proyecto
-`internationalizer detect` identifica tu configuración i18n comprobando:
+`internationalizer detect` identifica la configuración de su framework de i18n examinando:
-- Las dependencias de `package.json` para react-i18next, next-intl o vue-i18n
-- Estructuras de directorios que coinciden con patrones de locales comunes
+- Las dependencias del archivo `package.json` en busca de react-i18next, next-intl o vue-i18n
+- Estructuras de directorios que coincidan con patrones habituales de localización
- Extensiones de archivo y convenciones de nomenclatura
-
+
## Arquitectura
```
-cmd/internationalizer/ Punto de entrada de la CLI y definiciones de comandos
+cmd/internationalizer/ CLI entry point and command definitions
internal/
- config/ Carga de configuración YAML con valores por defecto
- detect/ Detección automática del tipo de proyecto
- formats/ Analizadores de formato (JSON, YAML, Markdown)
- glossary/ Gestión del glosario por locale
- llm/ Interfaz de proveedor de LLM + implementaciones
- anthropic.go Backend de Anthropic Claude
- openai.go Backend de OpenAI / compatible
- gemini.go Backend de Google Gemini vía AI Studio
- OpenRouter usa openai.go con base_url personalizada
- styleguide/ Cargador de guías de estilo
- tm/ Memoria de traducción JSONL
- translate/ Orquestador de traducción
- validate/ Validación de locales y diferencias
+ config/ YAML config loading with defaults
+ detect/ Project type auto-detection
+ formats/ Format parsers (JSON, YAML, Markdown)
+ glossary/ Per-locale glossary management
+ llm/ LLM provider interface + implementations
+ anthropic.go Anthropic Claude backend
+ openai.go OpenAI / compatible backend
+ gemini.go Google Gemini via AI Studio backend
+ OpenRouter uses openai.go with custom base_url
+ locale/ BCP 47 identity and CLDR plural categories
+ message/ ICU MessageFormat parser and structural comparison
+ policy/ Stable translation-policy hashing
+ state/ Versioned translation manifest
+ styleguide/ Style guide loader
+ tm/ JSONL translation memory
+ translate/ Translation orchestrator
+ validate/ Locale validation and diffing
```
-
+
## Comparación con alternativas
| Característica | Internationalizer | i18next | Crowdin | LLM genérico |
-|---------|------------------|---------|---------|-------------|
-| Traducción impulsada por LLM | Sí | No | Parcial | Sí |
+| --- | --- | --- | --- | --- |
+| Traducción mediante LLM | Sí | No | Parcial | Sí |
| Guías de estilo por idioma | Sí | No | No | No |
-| Aplicación de glosario | Sí | No | Sí | No |
+| Aplicación obligatoria de glosario | Sí | No | Sí | No |
| Memoria de traducción | Sí | No | Sí | No |
| CLI / ejecución local | Sí | N/A | No | Manual |
| Archivos compatibles con Git | Sí | Sí | Parcial | Manual |
-| Sin dependencia de SaaS | Sí | Sí | No | Varía |
-| Código abierto (AGPL-3.0) | Sí | Sí | No | Varía |
-
+| Sin dependencia de servicios SaaS | Sí | Sí | No | Variable |
+| Código abierto (AGPL-3.0) | Sí | Sí | No | Variable |
+
## Licencia
-[AGPL-3.0](LICENSE)
-
-## Contribución
+[AGPL-3.0](../../LICENSE)
-Consulta [CONTRIBUTING.md](CONTRIBUTING.md) para la configuración de desarrollo y las directrices. Todas las contribuciones requieren la firma del DCO.
+Consulte [THIRD_PARTY_NOTICES.md](../../THIRD_PARTY_NOTICES.md) para ver los avisos sobre dependencias de terceros.
+
+## Cómo contribuir
+Consulte [CONTRIBUTING.md](../../CONTRIBUTING.md) para obtener información sobre la configuración del entorno de desarrollo y las directrices de contribución. Todas las contribuciones requieren la firma del DCO.
diff --git a/docs/i18n/fi.md b/docs/i18n/fi.md
index 9139da9..c750e6a 100644
--- a/docs/i18n/fi.md
+++ b/docs/i18n/fi.md
@@ -6,33 +6,34 @@
# Internationalizer
-Tekoälypohjainen kansainvälistämisen työnkulku ohjelmistoprojekteille. Käännä, vahvista ja hallitse i18n-tiedostoja LLM-mallien avulla.
+Tekoälypohjainen kansainvälistämisen työnkulku ohjelmistoprojekteille. Käännä, validoi ja hallitse i18n-tiedostoja LLM-mallien avulla.
[](https://github.com/Tom-R-Main/Internationalizer/actions/workflows/ci.yml)
-[](LICENSE)
+[](../../LICENSE)
-العربية · বাংলা · Deutsch · Español · Français · हिन्दी · Indonesia · 日本語 · ਪੰਜਾਬੀ · Português · Русский · తెలుగు
-ไทย · Українська · 粵語 · 简体中文 · 繁體中文
+العربية · বাংলা · Čeština · Dansk · Deutsch · Ελληνικά · Español · Suomi · Français · עברית · हिन्दी · Indonesia · Italiano · 日本語 · 한국어 · Bahasa Melayu
Nederlands · ਪੰਜਾਬੀ · Polski · Português · Română · Русский · Svenska · తెలుగు · ไทย · Türkçe · Українська · Tiếng Việt · 粵語 · 简体中文 · 繁體中文
---
+
## Miksi Internationalizer?
-Useimmat i18n-työkalut ovat joko ajonaikaisia kirjastoja (i18next, react-intl) tai avaintenhallintaan tarkoitettuja SaaS-alustoja (Crowdin, Lokalise). Mikään niistä ei ratkaise varsinaista käännösongelmaa hyvin:
+Useimmat i18n-työkalut ovat joko ajonaikaisia kirjastoja (i18next, react-intl) tai avaintenhallintaan tarkoitettuja SaaS-alustoja (Crowdin, Lokalise). Mikään niistä ei ratkaise varsinaista käännösongelmaa kunnolla:
- **Manuaalinen kääntäminen** ei skaalaudu muutamaa kieltä pidemmälle
-- **Konekäännös-API:t** (Google Translate, DeepL) jättävät huomiotta terminologiasi, sävysi ja käyttöliittymäsi käytännöt
+- **Konekäännösrajapinnat** (Google Translate, DeepL) jättävät huomiotta terminologian, sävyn ja käyttöliittymäkäytännöt
- **Yleinen LLM-kääntäminen** toimii paremmin, mutta ilman sanastoja ja tyylioppaita tulokset ovat epäjohdonmukaisia
-Internationalizer on erilainen. Se on **CLI-työnkulku**, joka yhdistää LLM-kääntämisen seuraaviin ominaisuuksiin:
+Internationalizer tekee poikkeuksen. Se on **CLI-työnkulku**, joka yhdistää LLM-kääntämisen ja seuraavat ominaisuudet:
-- **Kielikohtaiset sanastot** — pakota johdonmukainen terminologia koko sovelluksessasi
-- **Kielikohtaiset tyylioppaat** — hallitse sävyä, muodollisuutta, monikkomuotoja ja typografiaa
-- **Käännösmuisti** — ohita muuttumattomat merkkijonot ja säästä rahaa API-kutsuissa
-- **Avainten vahvistus** — huomaa puuttuvat käännökset ja interpolaatiovirheet ennen julkaisua
+- **Kielikohtaiset sanastot** – valvo johdonmukaista terminologiaa koko sovelluksessa
+- **Kielikohtaiset tyylioppaat** – hallitse sävyä, muodollisuutta, monikkomuotoja ja typografiaa
+- **Käännösmuisti** – ohita muuttumattomat merkkijonot ja säästä rahaa API-kutsuissa
+- **Deterministinen validointi** – havaitse puuttuvat tai ylimääräiset avaimet, suojatun rakenteen poikkeamat, sanasto-ongelmat sekä monikko- tai ICU-virheet ennen julkaisua
+
## Asennus
Asenna npm:n kautta:
@@ -49,13 +50,13 @@ npx internationalizer --help
npm-paketti asentaa vastaavan valmiiksi käännetyn binäärin npm:stä alustakohtaisten valinnaisten riippuvuuksien kautta.
-Asenna Go:n avulla:
+Asenna Go-työkalulla:
```bash
go install github.com/Tom-R-Main/Internationalizer/cmd/internationalizer@latest
```
-Tai käännä lähdekoodista:
+Tai rakenna lähdekoodista:
```bash
git clone https://github.com/Tom-R-Main/Internationalizer.git
@@ -63,22 +64,28 @@ cd Internationalizer
go build -o internationalizer ./cmd/internationalizer
```
+
## npm-paketit
- Git-tunnisteiden ja npm-pakettien versioiden on vastattava toisiaan, esimerkiksi `v0.1.0` ja `0.1.0`
-- Juuripaketti `internationalizer` riippuu alustapaketeista, kuten `internationalizer-darwin-arm64`
+- Juuritason `internationalizer`-paketti riippuu alustapaketeista, kuten `internationalizer-darwin-arm64`
- Tuetut npm-kohteet: macOS arm64/x64, Linux arm64/x64, Windows x64
-- CI-julkaisu vaatii GitHub-salaisuuden nimeltä `NPM_TOKEN`
+- Julkaisu CI-ympäristöstä vaatii GitHub-salaisuuden nimeltä `NPM_TOKEN`
+
## Pika-aloitus
-1. Luo asetustiedosto projektisi juureen:
+1. Luo asetustiedosto projektin juureen:
```yaml
# .internationalizer.yml
source_locale: en
target_locales: [fr, de, es, ja]
-source_path: locales/en.json
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
llm:
provider: gemini
@@ -104,49 +111,78 @@ internationalizer translate --dry-run
internationalizer translate
```
-5. Vahvista kaikki kielitiedostot:
+5. Validoi kaikki kieliympäristöt:
```bash
internationalizer validate
```
+
## Komennot
### `translate`
-Etsi puuttuvat avaimet ja käännä ne LLM:n avulla.
+Etsi puuttuvat tai vanhentuneet avaimet ja käännä ne LLM:n avulla.
```bash
internationalizer translate # translate all locales
internationalizer translate -l fr # translate French only
internationalizer translate --dry-run # preview without API calls
+internationalizer translate --adopt-existing # baseline existing translations without API calls
+internationalizer translate --refresh-policy # refresh prompt/style/model-stale entries
internationalizer translate --batch-size 20 # smaller batches
internationalizer translate --concurrency 2 # fewer parallel calls
```
+Käännöstila raportoi toisistaan riippumatta puuttuvat (missing), lähdetiedoltaan vanhentuneet (source-stale), käytännöltään vanhentuneet (policy-stale), ajan tasalla olevat (current) ja manuaalisesti muokatut (manually edited) tilat, joten manuaalinen muokkaus ei voi piilottaa lähteen tai käytännön muutosta. Käytännöltään vanhentuneet arvot raportoidaan, mutta ne käännetään uudelleen vain lipulla `--refresh-policy`. Manuaalisesti muokattuja arvoja ei koskaan korvata automaattisesti. Käytä lippua `--adopt-existing`, kun otat manifestin käyttöön aiemmin tarkastetuille käännöksille tai kun hyväksyt tarkastetun manuaalisen muokkauksen nimenomaisesti uudeksi perustasoksi.
+
### `validate`
-Tarkista kaikki kielitiedostot puuttuvien avainten, ylimääräisten avainten ja interpolaatiovirheiden varalta.
+Tarkista kaikki kielialuetiedostot suhteessa niiden lähdepaketteihin. Oletusarvoinen validointi tarkistaa rakenteellisen kattavuuden (vaadittujen kohdeavaimien läsnäolon prosenttiosuuden), raportoi ylimääräiset avaimet varoituksina ja epäonnistuu, jos avaimia puuttuu, interpolaatioissa on ristiriitoja tai ICU MessageFormat -rakenne on virheellinen.
```bash
internationalizer validate # human-readable output
internationalizer validate --json # machine-readable JSON
internationalizer validate -q # exit code only
+internationalizer validate --strict # enforce translation quality rules
+internationalizer validate --require-state # require current manifest provenance
```
+`--strict` raportoi myös käännöskattavuuden. Lähdearvon kanssa identtinen kielellinen arvo katsotaan kääntämättömäksi, ellei sanastossa ole nimenomaista koko arvoa koskevaa merkintää, jossa lähde ja kohde ovat täysin samat; `ignore_case`-asetusta noudatetaan, mutta pidempään arvoon sisältyvä sanastotermi ei muodosta poikkeusta. Tiukka tila epäonnistuu, jos löytyy ylimääräisiä avaimia, lähteen kanssa identtisiä arvoja, muuttuneita interpolaatio-, HTML-, koodi- tai Markdown-linkkirakenteita, sanastorikkomuksia tai määritettyjen monikkomuotojen puutteita.
+
+`--require-state` tarkistaa kunkin kohteen `.internationalizer.lock`-tiedostoa vasten. Se epäonnistuu, jos avainta ei seurata tai jos sen tallennettu lähde, käännöskäytäntö tai kohteen tiiviste on vanhentunut. Se voidaan yhdistää lipun `--strict` kanssa.
+
+Ihmisille luettavat ja JSON-raportit käyttävät pysyviä havaintokoodeja:
+
+| Koodi | Merkitys |
+| --- | --- |
+| `missing_key` / `extra_key` | Lähde- ja kohdeavainten joukot eroavat toisistaan |
+| `blank_translation` | Ei-tyhjällä lähteellä on tyhjä kohde tiukassa tilassa |
+| `source_identical` | Tiukan tilan kielellinen arvo on kääntämätön |
+| `protected_structure_mismatch` | Interpolaatio-, HTML-, koodi- tai linkkirakenne on muuttunut |
+| `glossary_violation` | Hyväksyttyä kohdetermiä tai sen varianttia ei löytynyt |
+| `plural_form_missing` | Kielialueelle määritetty monikkomuoto puuttuu |
+| `icu_message_syntax` | Lähteen tai kohteen ICU-viesti on virheellisesti muotoiltu |
+| `icu_argument_mismatch` | ICU-argumenttien nimet, tyypit tai muotoilijatyylit eroavat |
+| `icu_selector_mismatch` | Valitsimet eroavat toisistaan tai monikkoluokka ei kelpaa kohdekielialueelle |
+| `untracked` | Kohteelle ei löydy tietuetta manifestista |
+| `source_stale` | Lähdesisältö on muuttunut tallennetun käännöksen jälkeen |
+| `policy_stale` | Luotu kehote tai malliasetukset ovat muuttuneet |
+| `target_modified` | Kohdesisältö poikkeaa manifestin tietueesta |
+
### `detect`
-Tunnista i18n-kehys automaattisesti ja ehdota asetuksia.
+Tunnista i18n-kehys automaattisesti ja ehdota määrityksiä.
```bash
internationalizer detect
```
-Tukee seuraavia: react-i18next, next-intl, vue-i18n, tavallinen JSON, markdown-asiakirjat.
+Tukee seuraavia: react-i18next, next-intl, vue-i18n, tavallinen JSON, Markdown-dokumentit.
### `glossary`
-Hallitse kielikohtaisia sanastotermejä, joita pakotetaan käännöksen aikana.
+Hallitse kielikohtaisia sanastotermejä, joiden käyttöä valvotaan käännöksen aikana.
```bash
internationalizer glossary list --locale fr
@@ -156,7 +192,7 @@ internationalizer glossary remove --locale fr --source "Dashboard"
### `tm`
-Hallitse käännösmuistia (aiemmin käännettyjen merkkijonojen JSONL-välimuisti).
+Hallitse käännösmuistia (aiemmin käännettyjen merkkijonojen JSONL-välimuistia).
```bash
internationalizer tm stats # show record counts
@@ -164,6 +200,7 @@ internationalizer tm export # dump as JSON
internationalizer tm clear --force # delete all records
```
+
## Asetusten viiteopas
```yaml
@@ -173,10 +210,23 @@ internationalizer tm clear --force # delete all records
source_locale: en
# Languages to translate into (required)
-target_locales: [fr, de, es, ja, zh-CN, ar]
-
-# Path to the source locale file (required)
-source_path: locales/en.json
+target_locales: [fr, de, es, ja, yue, zh-CN, zh-TW, ar]
+
+# One or more source-to-target mappings (required).
+# {locale} is replaced with each configured target locale.
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
+ - id: docs
+ source: README.md
+ target: docs/i18n/{locale}.md
+ format: markdown
+
+# Backward compatibility: source_path still maps targets to sibling files
+# such as locales/fr.json. Prefer bundles for new projects.
+# source_path: locales/en.json
# LLM provider settings
llm:
@@ -185,7 +235,7 @@ llm:
# Model name defaults by provider:
# anthropic: claude-opus-5
- # openai: gpt-5.6-luna
+ # openai: gpt-5.6-luna (reasoning effort defaults to max)
# gemini: gemini-3.8-flash
# openrouter: deepseek/deepseek-v4-pro-0813
model: gemini-3.8-flash
@@ -196,6 +246,27 @@ llm:
# Base URL for OpenAI-compatible endpoints (optional)
# base_url: https://api.openai.com
+ # OpenAI GPT-5-series Responses API reasoning effort
+ # (default: max for the OpenAI provider)
+ reasoning_effort: max
+
+ # Optional LLM settings for individual target locales. An override using the
+ # global provider inherits unspecified global settings. A different provider
+ # uses that provider's defaults for unspecified settings.
+ locale_overrides:
+ yue:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-CN:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-TW:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+
# Keys per LLM call (default: 40)
batch_size: 40
@@ -210,11 +281,26 @@ glossary_dir: glossary
# Path to translation memory file (default: .internationalizer/tm.jsonl)
tm_path: .internationalizer/tm.jsonl
+
+# Versioned source, policy, target, and provenance state
+# (default: .internationalizer.lock; commit this file)
+manifest_path: .internationalizer.lock
+
+# Optional translation and strict-validation rules
+validation:
+ plural_style: i18next-v4 # generate and validate target-locale plural forms
```
+Kielialuetunnisteiden on oltava oikein muodostettuja BCP 47 -tunnisteita, kuten `fr`, `pt-BR` tai `sr-Latn-RS`. Kanonisesti vastaavat kohdekielialueet hylätään kaksoiskappaleina, ja kielialuekohtaiset palveluntarjoajien ohitukset täsmäävät kanonisesti vastaavaan kirjoitusasuun. Yllä olevassa esimerkissä ohittamattomat kielialueet – mukaan lukien japani – perivät globaalit Gemini-asetukset.
+
+ICU MessageFormat -arvot jäsennetään rakenteellisesti. Yksinkertaisia argumentteja sekä tyyppejä `select`, `plural`, `selectordinal`, `number`, `date` ja `time` tuetaan, mukaan lukien sisäkkäiset viestit, monikkojen siirtymät (plural offsets), tarkat lukumäärävalitsimet ja `#`. Validointi tarkistaa syntaksin, argumenttien tyypit ja muotoilijatyylit, monikon siirtymät, select-haarojen identiteetin sekä kohdekielialueen CLDR-monikkoluokat. Palveluntarjoajan tuottama tuloste, joka rikkoo näitä invariantteja, hylätään ennen kuin kielialuetiedostoa tai käännösmuistitietuetta kirjoitetaan.
+
+Käytettäessä arvoa `i18next-v4` tunnistetut lähteen monikkoperheet laajennetaan käännöksen aikana kohdekielialueen CLDR-luokkiin. Vain kohteessa esiintyvä luokka käyttää lähdeperheen `_other`-arvoa käännösmallinaan. Tiukka validointi edellyttää näitä kohdeluokkia; vain lähteessä esiintyvät luokat ovat valinnaisia niille kohdekielialueille, jotka eivät käytä niitä.
+
+
## Tyylioppaat
-Tyylioppaat ovat Markdown-tiedostoja, jotka syötetään LLM-käännöspromptiin. Ne ohjaavat sävyä, muodollisuutta, typografiaa ja muita kielikohtaisia käytäntöjä.
+Tyylioppaat ovat Markdown-tiedostoja, jotka syötetään LLM-käännöskomentoon. Ne ohjaavat sävyä, muodollisuutta, typografiaa ja muita kielikohtaisia käytäntöjä.
```
style-guides/
@@ -226,58 +312,69 @@ style-guides/
### Jaetut käytännöt (`_conventions.md`)
-Määritä säännöt, jotka koskevat kaikkia kieliä: interpolaatiosyntaksi, HTML:n säilyttäminen, merkkijonotyyppien käytännöt (painikkeet vs. tunnisteet vs. virheet) jne.
+Määritä kaikkiin kieliin sovellettavat säännöt: interpolaatiosyntaksi, HTML:n säilyttäminen, merkkijonotyyppien käytännöt (painikkeet vs. selitteet vs. virheet) jne.
### Kielikohtaiset oppaat (`{locale}.md`)
-Määritä kielikohtaiset säännöt: muodollisuusrekisteri (sinuttelu vs. teitittely), välimerkit (lainausmerkit, ylösalaisin olevat kysymysmerkit), monikkomuodot, päivämäärien/numeroiden muotoilu ja terminologiasanasto.
+Määritä kielikohtaiset säännöt: muodollisuusrekisteri (sinuttelu vs. teitittely), välimerkit (lainausmerkit, ylösalaisin käännetyt kysymysmerkit), monikkomuodot, päivämäärien ja numeroiden muotoilu sekä terminologiasanasto.
-Katso toimiva esimerkki kohdasta [`examples/react-app/style-guides/`](examples/react-app/style-guides/).
+Tyylioppaat ovat pysyviä käytäntösyötteitä, eivät luotua tulostetta. Internationalizer lukee ne, muttei koskaan kirjoita niitä uudelleen. Niiden sisältö tiivistetään erikseen sanastosta ja kehotesopimuksesta, joten sovelluskoodin muutos ei tee käännöksestä vanhentunutta. Oppaan muokkaaminen merkitsee kyseisen kielialueen tarkoituksellisesti käytäntötarkastukseen; sisäisen kehotteen sanamuodon muuttaminen ei tee niin, ellei myös kehotesopimuksen versio muutu.
+Katso toimiva esimerkki kohdasta [`examples/react-app/style-guides/`](../../examples/react-app/style-guides/).
+
+
## Sanaston muoto
-Sanastotiedostot ovat JSON-taulukoita, jotka on tallennettu sijaintiin `{glossary_dir}/{locale}.json`:
+Sanastotiedostot ovat JSON-taulukoita, jotka tallennetaan polkuun `{glossary_dir}/{locale}.json`:
```json
[
{
"source": "Dashboard",
"target": "Tableau de bord",
+ "variants": ["Panneau de contrôle"],
+ "enforcement": "error",
"ignore_case": false,
"whole_word": true
}
]
```
-Termit syötetään LLM-promptiin terminologiataulukkona, mikä varmistaa avaintermien johdonmukaisen kääntämisen koko sovelluksessasi.
+`variants` luettelee muut hyväksytyt kohdemuodot. `enforcement`-kentän arvo voi olla `error`, `warning` tai se voidaan jättää pois, jolloin käytetään oletusarvoista error-toimintaa. Termit syötetään LLM-kehotteeseen terminologiataulukkona, mikä varmistaa johdonmukaisen kääntämisen läpi koko sovelluksen. Tarkka merkintä, kuten `{"source":"API","target":"API"}`, myös vapauttaa kyseisen täysin lähdeidenttisen arvon tiukan tilan kääntämätön arvo -havainnoista; se ei vapauta pidempää arvoa, joka vain sisältää termin `API`.
+
## Käännösmuisti
-Käännösmuisti tallennetaan JSONL-tiedostona (yksi JSON-tietue per rivi). Jokainen tietue sisältää seuraavat:
+Käännösmuisti tallennetaan JSONL-tiedostona (yksi JSON-tietue riviä kohden). Jokainen tietue sisältää seuraavat tiedot:
-- Lähdeavaimen ja -arvon
-- Käännetyn arvon
-- Lähdearvon SHA-256-tiivisteen
-- Aikaleiman
+- Paketti (bundle), avain, lähdearvo, käännetty arvo ja kanoninen kohdekielialue
+- Lähteen, tyylioppaan, sanaston, kehotesopimuksen ja yhdistetyn käytännön tiivisteet
+- Palveluntarjoaja ja malli, joka tuotti käännöksen
+- Aikaleima
-Seuraavilla suorituskerroilla muuttumattomat merkkijonot tarjoillaan TM-välimuistista ilman LLM-kutsua, mikä säästää sekä aikaa että API-kustannuksia. TM-tiedosto on Git-yhteensopiva ja se voidaan kommitoida kielitiedostojesi mukana.
+Seuraavilla suorituskerroilla merkkijonot, joilla on sama lähde- ja käytäntötiiviste, palautetaan välimuistista kutsumatta LLM-mallia. Oletuspolku sijaitsee huomiotta jätetyssä `.internationalizer/`-hakemistossa, joten se pysyy paikallisena välimuistina. Aseta `tm_path` seurattuun sijaintiin, jos projektisi jakaa käännösmuistin tarkoituksellisesti. Katselmoitava `.internationalizer.lock`-manifesti versioidaan erikseen.
-## Tuetut muodot
+
+## Tuetut tiedostomuodot
-| Muoto | Tunnisteet | Tila |
+| Muoto | Tiedostotunnisteet | Tila |
|--------|-----------|------|
| JSON | `.json` | Avain-arvo (sisäkkäinen, pistenotaatiolla litistetty) |
| YAML | `.yml`, `.yaml` | Avain-arvo (säilyttää kommentit ja järjestyksen) |
-| Markdown | `.md`, `.mdx` | Koko asiakirjan käännös |
+| Markdown | `.md`, `.mdx` | Johdanto-osa ja H2-tason osiot |
+
+Markdown-kohteet sisältävät näkymättömiä `internationalizer:unit`-kommentteja ennen H2-osioita. Näiden pysyvien merkintöjen ansiosta Internationalizer voi lisätä, siirtää tai muokata yhtä lähdeosiota kääntämättä toisiinsa liittymättömiä osioita uudelleen. Olemassa olevat merkitsemättömät asiakirjat saavat merkinnät seuraavan onnistuneen päivityksen yhteydessä.
+
## Projektityypin tunnistus
-`internationalizer detect` tunnistaa i18n-asetuksesi tarkistamalla seuraavat:
+`internationalizer detect` tunnistaa i18n-määrityksesi tarkistamalla seuraavat asiat:
- `package.json`-riippuvuudet (react-i18next, next-intl tai vue-i18n)
-- Yleisiä kieliasetusten malleja vastaavat hakemistorakenteet
-- Tiedostotunnisteet ja nimeämiskäytännöt
+- Yleisiä kielialuemalleja vastaavat hakemistorakenteet
+- Tiedostopäätteet ja nimeämiskäytännöt
+
## Arkkitehtuuri
```
@@ -292,30 +389,38 @@ internal/
openai.go OpenAI / compatible backend
gemini.go Google Gemini via AI Studio backend
OpenRouter uses openai.go with custom base_url
+ locale/ BCP 47 identity and CLDR plural categories
+ message/ ICU MessageFormat parser and structural comparison
+ policy/ Stable translation-policy hashing
+ state/ Versioned translation manifest
styleguide/ Style guide loader
tm/ JSONL translation memory
translate/ Translation orchestrator
validate/ Locale validation and diffing
```
+
## Vertailu vaihtoehtoihin
| Ominaisuus | Internationalizer | i18next | Crowdin | Yleinen LLM |
|---------|------------------|---------|---------|-------------|
| LLM-pohjainen käännös | Kyllä | Ei | Osittainen | Kyllä |
| Kielikohtaiset tyylioppaat | Kyllä | Ei | Ei | Ei |
-| Sanaston pakotus | Kyllä | Ei | Kyllä | Ei |
+| Sanaston valvonta | Kyllä | Ei | Kyllä | Ei |
| Käännösmuisti | Kyllä | Ei | Kyllä | Ei |
-| CLI / paikallinen suoritus | Kyllä | Ei sovellu | Ei | Manuaalinen |
-| Git-yhteensopivat tiedostot | Kyllä | Kyllä | Osittainen | Manuaalinen |
+| CLI- / paikallinen suoritus | Kyllä | Ei sovellu | Ei | Manuaalinen |
+| Git-ystävälliset tiedostot | Kyllä | Kyllä | Osittainen | Manuaalinen |
| Ei SaaS-riippuvuutta | Kyllä | Kyllä | Ei | Vaihtelee |
| Avoin lähdekoodi (AGPL-3.0) | Kyllä | Kyllä | Ei | Vaihtelee |
+
## Lisenssi
-[AGPL-3.0](LICENSE)
+[AGPL-3.0](../../LICENSE)
-## Osallistuminen
+Katso riippuvuuksien ilmoitukset tiedostosta [THIRD_PARTY_NOTICES.md](../../THIRD_PARTY_NOTICES.md).
-Katso kehitysympäristön asennusohjeet ja suuntaviivat tiedostosta [CONTRIBUTING.md](CONTRIBUTING.md). Kaikki osallistuminen vaatii DCO-allekirjoituksen.
+
+## Osallistuminen
+Katso kehitysympäristön määritys ja ohjeet tiedostosta [CONTRIBUTING.md](../../CONTRIBUTING.md). Kaikki kontribuutiot vaativat DCO-hyväksynnän.
diff --git a/docs/i18n/fr.md b/docs/i18n/fr.md
index 8b4a1b2..2873c6c 100644
--- a/docs/i18n/fr.md
+++ b/docs/i18n/fr.md
@@ -6,26 +6,32 @@
# Internationalizer
-Pipeline d'internationalisation natif IA pour les projets logiciels. Traduisez, validez et gérez les fichiers i18n à l'aide de LLM.
+Pipeline d'internationalisation natif pour l'IA destiné aux projets logiciels. Traduisez, validez et gérez les fichiers i18n à l'aide de LLM.
[](https://github.com/Tom-R-Main/Internationalizer/actions/workflows/ci.yml)
-[](LICENSE)
+[](../../LICENSE)
+
+العربية · বাংলা · Čeština · Dansk · Deutsch · Ελληνικά · Español · Suomi · Français · עברית · हिन्दी · Indonesia · Italiano · 日本語 · 한국어 · Bahasa Melayu
Nederlands · ਪੰਜਾਬੀ · Polski · Português · Română · Русский · Svenska · తెలుగు · ไทย · Türkçe · Українська · Tiếng Việt · 粵語 · 简体中文 · 繁體中文
+
+
+---
+
## Pourquoi Internationalizer ?
-La plupart des outils i18n sont soit des bibliothèques d'exécution (i18next, react-intl), soit des plateformes SaaS de gestion de clés (Crowdin, Lokalise). Aucun d'entre eux ne résout correctement le véritable problème de la traduction :
+La plupart des outils i18n sont soit des bibliothèques d'exécution (i18next, react-intl), soit des plateformes SaaS de gestion de clés (Crowdin, Lokalise). Aucun d'entre eux ne résout correctement le véritable problème de traduction :
- **La traduction manuelle** ne passe pas à l'échelle au-delà de quelques langues
- **Les API de traduction automatique** (Google Translate, DeepL) ignorent votre terminologie, votre ton et vos conventions d'interface utilisateur
-- **La traduction LLM générique** fonctionne mieux, mais sans glossaires ni guides de style, vous obtenez des résultats incohérents
+- **La traduction par LLM générique** fonctionne mieux, mais sans glossaires ni guides de style, vous obtenez des résultats incohérents
-Internationalizer est différent. C'est un **pipeline CLI** qui combine la traduction LLM avec :
+Internationalizer est différent. Il s'agit d'un **pipeline CLI** qui combine la traduction par LLM avec :
-- **Des glossaires par langue** — appliquent une terminologie cohérente dans toute votre application
+- **Des glossaires par langue** — appliquent une terminologie cohérente dans l'ensemble de votre application
- **Des guides de style par langue** — contrôlent le ton, le niveau de formalité, la pluralisation et la typographie
-- **Une mémoire de traduction** — ignore les chaînes inchangées, économise de l'argent sur les appels d'API
-- **La validation des clés** — détecte les traductions manquantes et les incohérences d'interpolation avant le déploiement
-
+- **Une mémoire de traduction** — ignore les chaînes inchangées, réduisant les coûts d'appels d'API
+- **Une validation déterministe** — intercepte les clés manquantes ou superflues, les dérives de structures protégées, les non-respects du glossaire, ainsi que les erreurs de pluriel ou ICU avant la mise en production
+
## Installation
Installer depuis npm :
@@ -48,21 +54,21 @@ Installer avec Go :
go install github.com/Tom-R-Main/Internationalizer/cmd/internationalizer@latest
```
-Ou compiler depuis les sources :
+Ou compiler à partir des sources :
```bash
git clone https://github.com/Tom-R-Main/Internationalizer.git
cd Internationalizer
go build -o internationalizer ./cmd/internationalizer
```
-
+
## Paquets npm
-- Les tags Git et les versions des paquets npm doivent correspondre, par exemple `v0.1.0` et `0.1.0`
+- Les tags Git et les versions de paquets npm doivent correspondre, par exemple `v0.1.0` et `0.1.0`
- Le paquet racine `internationalizer` dépend de paquets de plateforme tels que `internationalizer-darwin-arm64`
- Cibles npm prises en charge : macOS arm64/x64, Linux arm64/x64, Windows x64
- La publication CI nécessite un secret GitHub nommé `NPM_TOKEN`
-
+
## Démarrage rapide
1. Créez un fichier de configuration à la racine de votre projet :
@@ -71,7 +77,11 @@ go build -o internationalizer ./cmd/internationalizer
# .internationalizer.yml
source_locale: en
target_locales: [fr, de, es, ja]
-source_path: locales/en.json
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
llm:
provider: gemini
@@ -102,44 +112,72 @@ internationalizer translate
```bash
internationalizer validate
```
-
+
## Commandes
### `translate`
-Trouve les clés manquantes et les traduit via un LLM.
+Trouvez les clés manquantes ou obsolètes et traduisez-les via un LLM.
```bash
-internationalizer translate # traduire toutes les locales
-internationalizer translate -l fr # traduire uniquement le français
-internationalizer translate --dry-run # prévisualiser sans appels d'API
-internationalizer translate --batch-size 20 # lots plus petits
-internationalizer translate --concurrency 2 # moins d'appels parallèles
+internationalizer translate # translate all locales
+internationalizer translate -l fr # translate French only
+internationalizer translate --dry-run # preview without API calls
+internationalizer translate --adopt-existing # baseline existing translations without API calls
+internationalizer translate --refresh-policy # refresh prompt/style/model-stale entries
+internationalizer translate --batch-size 20 # smaller batches
+internationalizer translate --concurrency 2 # fewer parallel calls
```
+L'état de la traduction signale de manière indépendante les conditions manquante, source-stale, policy-stale, actuelle et modifiée manuellement, de sorte qu'une modification manuelle ne puisse dissimuler un changement de source ou de stratégie. Les valeurs policy-stale sont signalées mais ne sont retraduites qu'avec `--refresh-policy`. Les valeurs modifiées manuellement ne sont jamais écrasées automatiquement. Utilisez `--adopt-existing` lors de l'intégration du manifeste à des traductions révisées ou lors de l'acceptation explicite d'une modification manuelle révisée en tant que nouvelle référence.
+
### `validate`
-Vérifie tous les fichiers de locale pour trouver les clés manquantes, les clés en trop et les incohérences d'interpolation.
+Vérifiez tous les fichiers de locale par rapport à leurs bundles sources. La validation par défaut contrôle la couverture structurelle (le pourcentage de clés cibles requises présentes), signale les clés superflues comme des avertissements et échoue en cas de clés manquantes, de discordances d'interpolation ou de structure ICU MessageFormat non valide.
```bash
-internationalizer validate # sortie lisible par un humain
-internationalizer validate --json # JSON lisible par une machine
-internationalizer validate -q # code de sortie uniquement
+internationalizer validate # human-readable output
+internationalizer validate --json # machine-readable JSON
+internationalizer validate -q # exit code only
+internationalizer validate --strict # enforce translation quality rules
+internationalizer validate --require-state # require current manifest provenance
```
+`--strict` signale également la couverture traduite. Une valeur linguistique identique à sa source est considérée comme non traduite, à moins que le glossaire ne contienne explicitement une entrée exacte avec source identique et cible identique pour la valeur complète ; `ignore_case` est respecté, mais un terme de glossaire intégré dans une valeur plus longue ne constitue pas une exemption. Le mode strict échoue en cas de clés superflues, de valeurs identiques à la source, de modifications de structure d'interpolation/HTML/code/liens Markdown, d'infractions au glossaire et de formes de pluriel configurées manquantes.
+
+`--require-state` vérifie chaque cible par rapport à `.internationalizer.lock`. Il échoue lorsqu'une clé n'est pas suivie, ou lorsque sa source enregistrée, sa stratégie de traduction ou son hachage cible est obsolète. Il peut être combiné avec `--strict`.
+
+Les rapports lisibles par un humain et JSON utilisent des codes de constatation stables :
+
+| Code | Signification |
+| --- | --- |
+| `missing_key` / `extra_key` | Les ensembles de clés source et cible diffèrent |
+| `blank_translation` | Une source non vide a une cible vide en mode strict |
+| `source_identical` | Une valeur linguistique en mode strict reste non traduite |
+| `protected_structure_mismatch` | La structure d'interpolation, de HTML, de code ou de lien a changé |
+| `glossary_violation` | Aucun terme cible ou variante approuvé n'a été trouvé |
+| `plural_form_missing` | Une forme de pluriel configurée pour la locale est absente |
+| `icu_message_syntax` | Un message ICU source ou cible est malformé |
+| `icu_argument_mismatch` | Les noms, les types ou les styles de formateur des arguments ICU diffèrent |
+| `icu_selector_mismatch` | Les sélecteurs diffèrent ou une catégorie de pluriel n'est pas valide pour la locale cible |
+| `untracked` | Aucun enregistrement de manifeste n'existe pour la cible |
+| `source_stale` | Le contenu source a changé après la traduction enregistrée |
+| `policy_stale` | Le prompt généré ou les paramètres du modèle ont changé |
+| `target_modified` | Le contenu cible diffère de l'enregistrement dans le manifeste |
+
### `detect`
-Détecte automatiquement le framework i18n et suggère une configuration.
+Détectez automatiquement le framework i18n et suggérez une configuration.
```bash
internationalizer detect
```
-Prend en charge : react-i18next, next-intl, vue-i18n, JSON natif, documents Markdown.
+Prend en charge : react-i18next, next-intl, vue-i18n, JSON standard, documents Markdown.
### `glossary`
-Gère les termes du glossaire par langue qui sont appliqués lors de la traduction.
+Gérez les termes de glossaire par langue qui sont appliqués lors de la traduction.
```bash
internationalizer glossary list --locale fr
@@ -149,84 +187,134 @@ internationalizer glossary remove --locale fr --source "Dashboard"
### `tm`
-Gère la mémoire de traduction (cache JSONL des chaînes précédemment traduites).
+Gérez la mémoire de traduction (cache JSONL des chaînes précédemment traduites).
```bash
-internationalizer tm stats # afficher le nombre d'enregistrements
-internationalizer tm export # exporter en JSON
-internationalizer tm clear --force # supprimer tous les enregistrements
+internationalizer tm stats # show record counts
+internationalizer tm export # dump as JSON
+internationalizer tm clear --force # delete all records
```
-
+
## Référence de configuration
```yaml
# .internationalizer.yml
-# Langue source (par défaut : en)
+# Source language (default: en)
source_locale: en
-# Langues vers lesquelles traduire (requis)
-target_locales: [fr, de, es, ja, zh-CN, ar]
-
-# Chemin vers le fichier de locale source (requis)
-source_path: locales/en.json
-
-# Paramètres du fournisseur LLM
+# Languages to translate into (required)
+target_locales: [fr, de, es, ja, yue, zh-CN, zh-TW, ar]
+
+# One or more source-to-target mappings (required).
+# {locale} is replaced with each configured target locale.
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
+ - id: docs
+ source: README.md
+ target: docs/i18n/{locale}.md
+ format: markdown
+
+# Backward compatibility: source_path still maps targets to sibling files
+# such as locales/fr.json. Prefer bundles for new projects.
+# source_path: locales/en.json
+
+# LLM provider settings
llm:
- # Fournisseur : "anthropic", "openai", "gemini" ou "openrouter" (par défaut : gemini)
+ # Provider: "anthropic", "openai", "gemini", or "openrouter" (default: gemini)
provider: gemini
- # Noms de modèles par défaut selon le fournisseur :
+ # Model name defaults by provider:
# anthropic: claude-opus-5
- # openai: gpt-5.6-luna
+ # openai: gpt-5.6-luna (reasoning effort defaults to max)
# gemini: gemini-3.8-flash
# openrouter: deepseek/deepseek-v4-pro-0813
model: gemini-3.8-flash
- # Variable d'environnement contenant la clé d'API
+ # Environment variable containing the API key
api_key_env: GOOGLE_AI_STUDIO_API_KEY
- # URL de base pour les points de terminaison compatibles OpenAI (optionnel)
+ # Base URL for OpenAI-compatible endpoints (optional)
# base_url: https://api.openai.com
-# Clés par appel LLM (par défaut : 40)
+ # OpenAI GPT-5-series Responses API reasoning effort
+ # (default: max for the OpenAI provider)
+ reasoning_effort: max
+
+ # Optional LLM settings for individual target locales. An override using the
+ # global provider inherits unspecified global settings. A different provider
+ # uses that provider's defaults for unspecified settings.
+ locale_overrides:
+ yue:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-CN:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-TW:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+
+# Keys per LLM call (default: 40)
batch_size: 40
-# Appels LLM parallèles (par défaut : 4)
+# Parallel LLM calls (default: 4)
concurrency: 4
-# Répertoire contenant les fichiers Markdown des guides de style par locale (par défaut : style-guides)
+# Directory containing per-locale style guide Markdown files (default: style-guides)
style_guides_dir: style-guides
-# Répertoire contenant les fichiers JSON du glossaire par locale (par défaut : glossary)
+# Directory containing per-locale glossary JSON files (default: glossary)
glossary_dir: glossary
-# Chemin vers le fichier de mémoire de traduction (par défaut : .internationalizer/tm.jsonl)
+# Path to translation memory file (default: .internationalizer/tm.jsonl)
tm_path: .internationalizer/tm.jsonl
+
+# Versioned source, policy, target, and provenance state
+# (default: .internationalizer.lock; commit this file)
+manifest_path: .internationalizer.lock
+
+# Optional translation and strict-validation rules
+validation:
+ plural_style: i18next-v4 # generate and validate target-locale plural forms
```
+Les identifiants de locale doivent être des balises BCP 47 bien formées telles que `fr`, `pt-BR` ou `sr-Latn-RS`. Les locales cibles équivalentes au sens canonique sont rejetées en tant que doublons, et les surcharges de fournisseur spécifiques à une locale s'alignent sur la graphie canonique équivalente. Dans l'exemple ci-dessus, les locales sans surcharge — y compris le japonais — héritent de la configuration globale Gemini.
+
+Les valeurs ICU MessageFormat sont analysées structurellement. Les arguments simples, `select`, `plural`, `selectordinal`, `number`, `date` et `time` sont pris en charge, y compris les messages imbriqués, les décalages de pluriel, les sélecteurs de nombres exacts et `#`. La validation vérifie la syntaxe, les types d'arguments et les styles de formateur, les décalages de pluriel, l'identité des branches select, ainsi que les catégories de pluriel CLDR de la locale cible. Les résultats du fournisseur qui enfreignent ces invariants sont rejetés avant l'écriture d'un fichier de locale ou d'un enregistrement de mémoire de traduction.
+
+Avec `i18next-v4`, les familles de pluriels sources reconnues sont développées lors de la traduction selon les catégories CLDR de la locale cible. Une catégorie propre à la cible utilise la valeur `_other` de la famille source comme modèle de traduction. La validation stricte exige ces catégories cibles ; les catégories propres à la source sont facultatives pour les locales cibles qui ne les utilisent pas.
+
## Guides de style
-Les guides de style sont des fichiers Markdown qui sont injectés dans le prompt de traduction du LLM. Ils contrôlent le ton, le niveau de formalité, la typographie et d'autres conventions spécifiques à la langue.
+Les guides de style sont des fichiers Markdown injectés dans le prompt de traduction du LLM. Ils contrôlent le ton, le niveau de formalité, la typographie et d'autres conventions propres à chaque langue.
```
style-guides/
- _conventions.md # règles partagées pour toutes les langues
- fr.md # règles spécifiques au français
- ja.md # règles spécifiques au japonais
- ar.md # règles spécifiques à l'arabe
+ _conventions.md # shared rules for all languages
+ fr.md # French-specific rules
+ ja.md # Japanese-specific rules
+ ar.md # Arabic-specific rules
```
### Conventions partagées (`_conventions.md`)
-Définissez les règles qui s'appliquent à toutes les langues : syntaxe d'interpolation, préservation du HTML, conventions de type de chaîne (boutons vs étiquettes vs erreurs), etc.
+Définissez les règles qui s'appliquent à toutes les langues : syntaxe d'interpolation, préservation du code HTML, conventions sur les types de chaînes (boutons vs étiquettes vs erreurs), etc.
### Guides par langue (`{locale}.md`)
-Définissez les règles spécifiques à la langue : registre de formalité (tu vs vous), ponctuation (guillemets, points d'interrogation inversés), formes plurielles, formatage des dates/nombres et un glossaire terminologique.
+Définissez les règles propres à chaque langue : registre de formalité (tu vs vous), ponctuation (guillemets, points d'interrogation inversés), formes de pluriel, formatage des dates et nombres, ainsi qu'un glossaire terminologique.
-Consultez [`examples/react-app/style-guides/`](examples/react-app/style-guides/) pour un exemple fonctionnel.
+Les guides de style constituent des entrées de stratégie durables et non des sorties générées. Internationalizer les lit mais ne les réécrit jamais. Leur contenu est haché séparément du glossaire et du contrat de prompt, de sorte qu'une modification du code de l'application ne rende pas une traduction obsolète. L'édition d'un guide marque délibérément cette locale pour révision de stratégie ; la modification de la formulation interne du prompt ne le fait pas, sauf si la version du contrat de prompt change également.
+Consultez [`examples/react-app/style-guides/`](../../examples/react-app/style-guides/) pour un exemple fonctionnel.
+
## Format du glossaire
Les fichiers de glossaire sont des tableaux JSON stockés dans `{glossary_dir}/{locale}.json` :
@@ -236,79 +324,88 @@ Les fichiers de glossaire sont des tableaux JSON stockés dans `{glossary_dir}/{
{
"source": "Dashboard",
"target": "Tableau de bord",
+ "variants": ["Panneau de contrôle"],
+ "enforcement": "error",
"ignore_case": false,
"whole_word": true
}
]
```
-Les termes sont injectés dans le prompt du LLM sous forme de table terminologique, garantissant une traduction cohérente des termes clés dans toute votre application.
-
+`variants` répertorie les autres formes cibles approuvées. `enforcement` peut prendre la valeur `error`, `warning`, ou être omis pour appliquer le comportement d'erreur par défaut. Les termes sont injectés dans le prompt du LLM sous forme de tableau terminologique, garantissant ainsi une traduction cohérente dans toute votre application. Une entrée exacte telle que `{"source":"API","target":"API"}` exempte également cette valeur complète identique à la source des constats de valeurs non traduites en mode strict ; elle n'exempte pas une valeur plus longue contenant simplement `API`.
+
## Mémoire de traduction
La mémoire de traduction est stockée sous forme de fichier JSONL (un enregistrement JSON par ligne). Chaque enregistrement contient :
-- La clé et la valeur sources
-- La valeur traduite
-- Un hachage SHA-256 de la valeur source
+- Le bundle, la clé, la valeur source, la valeur traduite et la locale cible canonique
+- Les hachages de la source, du guide de style, du glossaire, du contrat de prompt et de la stratégie combinée
+- Le fournisseur et le modèle qui ont produit la traduction
- Un horodatage
-Lors des exécutions ultérieures, les chaînes inchangées sont servies depuis le cache de la MT sans appeler le LLM, ce qui permet d'économiser du temps et des coûts d'API. Le fichier de MT est adapté à Git et peut être commité avec vos fichiers de locale.
-
+Lors des exécutions ultérieures, les chaînes présentant les mêmes hachages de source et de stratégie sont servies depuis le cache sans faire appel au LLM. Le chemin par défaut se situe sous le répertoire ignoré `.internationalizer/`, de sorte qu'il reste un cache local. Définissez `tm_path` vers un emplacement suivi si votre projet partage intentionnellement la mémoire de traduction. Le manifeste révisable `.internationalizer.lock` est versionné séparément.
+
## Formats pris en charge
| Format | Extensions | Mode |
-|--------|-----------|------|
-| JSON | `.json` | Clé-valeur (imbriqué, aplati par notation pointée) |
+| --- | --- | --- |
+| JSON | `.json` | Clé-valeur (imbriqué, aplati en notation avec points) |
| YAML | `.yml`, `.yaml` | Clé-valeur (préserve les commentaires et l'ordre) |
-| Markdown | `.md`, `.mdx` | Traduction de document entier |
+| Markdown | `.md`, `.mdx` | Préambule et sections de niveau H2 |
+Les cibles Markdown contiennent des commentaires invisibles `internationalizer:unit` avant les sections H2. Ces repères stables permettent à Internationalizer d'ajouter, déplacer ou modifier une section source sans retraduire les sections non concernées. Les documents existants non marqués reçoivent ces repères lors de leur prochaine mise à jour réussie.
+
## Détection du type de projet
`internationalizer detect` identifie votre configuration i18n en vérifiant :
-- Les dépendances `package.json` pour react-i18next, next-intl ou vue-i18n
+- Les dépendances dans `package.json` pour react-i18next, next-intl ou vue-i18n
- Les structures de répertoires correspondant aux modèles de locales courants
-- Les extensions de fichiers et les conventions de nommage
-
+- Les extensions de fichiers et conventions de nommage
+
## Architecture
```
-cmd/internationalizer/ Point d'entrée CLI et définitions des commandes
+cmd/internationalizer/ CLI entry point and command definitions
internal/
- config/ Chargement de la configuration YAML avec valeurs par défaut
- detect/ Détection automatique du type de projet
- formats/ Analyseurs de format (JSON, YAML, Markdown)
- glossary/ Gestion du glossaire par locale
- llm/ Interface du fournisseur LLM + implémentations
- anthropic.go Backend Anthropic Claude
- openai.go Backend OpenAI / compatible
- gemini.go Backend Google Gemini via AI Studio
- OpenRouter utilise openai.go avec une base_url personnalisée
- styleguide/ Chargeur de guide de style
- tm/ Mémoire de traduction JSONL
- translate/ Orchestrateur de traduction
- validate/ Validation et comparaison des locales
+ config/ YAML config loading with defaults
+ detect/ Project type auto-detection
+ formats/ Format parsers (JSON, YAML, Markdown)
+ glossary/ Per-locale glossary management
+ llm/ LLM provider interface + implementations
+ anthropic.go Anthropic Claude backend
+ openai.go OpenAI / compatible backend
+ gemini.go Google Gemini via AI Studio backend
+ OpenRouter uses openai.go with custom base_url
+ locale/ BCP 47 identity and CLDR plural categories
+ message/ ICU MessageFormat parser and structural comparison
+ policy/ Stable translation-policy hashing
+ state/ Versioned translation manifest
+ styleguide/ Style guide loader
+ tm/ JSONL translation memory
+ translate/ Translation orchestrator
+ validate/ Locale validation and diffing
```
-
-## Comparaison avec les alternatives
+
+## Comparaison avec d'autres solutions
| Fonctionnalité | Internationalizer | i18next | Crowdin | LLM générique |
-|---------|------------------|---------|---------|-------------|
-| Traduction par LLM | Oui | Non | Partiel | Oui |
+| --- | --- | --- | --- | --- |
+| Traduction basée sur l'IA / LLM | Oui | Non | Partiel | Oui |
| Guides de style par langue | Oui | Non | Non | Non |
-| Application du glossaire | Oui | Non | Oui | Non |
+| Application des glossaires | Oui | Non | Oui | Non |
| Mémoire de traduction | Oui | Non | Oui | Non |
-| Exécution CLI / locale | Oui | N/A | Non | Manuel |
-| Fichiers adaptés à Git | Oui | Oui | Partiel | Manuel |
+| Exécution en CLI / locale | Oui | N/A | Non | Manuel |
+| Fichiers compatibles avec Git | Oui | Oui | Partiel | Manuel |
| Aucune dépendance SaaS | Oui | Oui | Non | Variable |
| Open source (AGPL-3.0) | Oui | Oui | Non | Variable |
-
+
## Licence
-[AGPL-3.0](LICENSE)
+[AGPL-3.0](../../LICENSE)
+Consultez [THIRD_PARTY_NOTICES.md](../../THIRD_PARTY_NOTICES.md) pour les mentions relatives aux dépendances tierces.
+
## Contribution
-Consultez [CONTRIBUTING.md](CONTRIBUTING.md) pour la configuration de développement et les directives. Toutes les contributions nécessitent une approbation DCO.
-
+Consultez [CONTRIBUTING.md](../../CONTRIBUTING.md) pour la configuration du développement et les directives. Toutes les contributions requièrent la signature du DCO.
diff --git a/docs/i18n/he.md b/docs/i18n/he.md
index 4bb7401..dc46b5a 100644
--- a/docs/i18n/he.md
+++ b/docs/i18n/he.md
@@ -6,36 +6,35 @@
# Internationalizer
-תהליך עבודה (pipeline) מבוסס AI לבינאום (internationalization) של פרויקטי תוכנה. תרגום, אימות וניהול של קובצי i18n באמצעות מודלי LLM.
+צינור עבודה (pipeline) מבוסס AI לבינאום פרויקטי תוכנה. תרגום, אימות וניהול קובצי i18n באמצעות מודלי LLM.
[](https://github.com/Tom-R-Main/Internationalizer/actions/workflows/ci.yml)
-[](LICENSE)
+[](../../LICENSE)
-العربية · বাংলা · Deutsch · Español · Français · हिन्दी · Indonesia · 日本語 · ਪੰਜਾਬੀ · Português · Русский · తెలుగు
-ไทย · Українська · 粵語 · 简体中文 · 繁體中文
+العربية · বাংলা · Čeština · Dansk · Deutsch · Ελληνικά · Español · Suomi · Français · עברית · हिन्दी · Indonesia · Italiano · 日本語 · 한국어 · Bahasa Melayu
Nederlands · ਪੰਜਾਬੀ · Polski · Português · Română · Русский · Svenska · తెలుగు · ไทย · Türkçe · Українська · Tiếng Việt · 粵語 · 简体中文 · 繁體中文
---
+
+## מדוע Internationalizer?
-## למה לבחור ב-Internationalizer?
+רוב כלי ה-i18n הם ספריות זמן ריצה (כגון i18next או react-intl) או פלטפורמות SaaS לניהול מפתחות (כגון Crowdin או Lokalise). אף אחד מהם אינו פותר היטב את בעיית התרגום עצמה:
-רוב כלי ה-i18n הם ספריות זמן ריצה (runtime) כמו i18next ו-react-intl, או פלטפורמות SaaS לניהול מפתחות (Crowdin, Lokalise). אף אחד מהם לא פותר היטב את בעיית התרגום עצמה:
+- **תרגום ידני** אינו מדרגי מעבר למספר שפות בודדות
+- **ממשקי API לתרגום מכונה** (Google Translate, DeepL) מתעלמים מהמונחים, מהטון וממוסכמות ממשק המשתמש (UI)
+- **תרגום באמצעות LLM כללי** מספק תוצאות טובות יותר, אך ללא מילוני מונחים ומדריכי סגנון, מתקבלות תוצאות חסרות עקביות
-- **תרגום ידני** אינו סקיילבילי מעבר למספר שפות בודדות
-- **ממשקי API לתרגום מכונה** (Google Translate, DeepL) מתעלמים מהמונחים, מהטון וממוסכמות ממשק המשתמש (UI) שלכם
-- **תרגום LLM גנרי** עובד טוב יותר, אך ללא מילוני מונחים ומדריכי סגנון, מתקבלות תוצאות חסרות עקביות
-
-Internationalizer הוא שונה. זהו **CLI pipeline** המשלב תרגום LLM עם:
-
-- **מילוני מונחים לכל שפה** — אכיפת טרמינולוגיה עקבית ברחבי האפליקציה
-- **מדריכי סגנון לכל שפה** — שליטה בטון, ברמת הרשמיות, בצורות ריבוי ובטיפוגרפיה
-- **זיכרון תרגום (Translation memory)** — דילוג על מחרוזות שלא השתנו וחיסכון בעלויות קריאות API
-- **אימות מפתחות** — איתור תרגומים חסרים וחוסר התאמה במשתני אינטרפולציה לפני השחרור ללקוחות
+Internationalizer פועל אחרת. זהו **צינור עבודה מבוסס CLI** המשלב תרגום מבוסס LLM עם:
+- **מילוני מונחים ייעודיים לכל שפה** — אכיפת טרמינולוגיה עקבית ברחבי היישום
+- **מדריכי סגנון ייעודיים לכל שפה** — שליטה בטון, ברמת הרשמיות, בריבוי ובטיפוגרפיה
+- **זיכרון תרגום (Translation memory)** — דילוג על מחרוזות שלא השתנו וחיסכון בעלויות של קריאות API
+- **אימות דטרמיניסטי** — איתור מפתחות חסרים או עודפים, סטיות במבנים מוגנים, חריגות ממילון המונחים ושגיאות ריבוי או ICU לפני השחרור לגרסת הייצור
+
## התקנה
-התקנה מ-npm:
+התקנה דרך npm:
```bash
npm install -g internationalizer
@@ -47,7 +46,7 @@ npm install -g internationalizer
npx internationalizer --help
```
-חבילת ה-npm מתקינה את הקובץ הבינארי המקומפל מראש התואם למערכת ההפעלה מ-npm דרך תלויות אופציונליות ספציפיות לפלטפורמה.
+חבילת ה-npm מתקינה מ-npm את הקובץ הבינארי התואם שנבנה מראש, באמצעות תלויות רשות התלויות בפלטפורמה.
התקנה באמצעות Go:
@@ -55,22 +54,22 @@ npx internationalizer --help
go install github.com/Tom-R-Main/Internationalizer/cmd/internationalizer@latest
```
-או בנייה מקוד המקור:
+או הידור מקוד המקור:
```bash
git clone https://github.com/Tom-R-Main/Internationalizer.git
cd Internationalizer
go build -o internationalizer ./cmd/internationalizer
```
-
+
## חבילות npm
-- תגיות Git וגרסאות חבילת npm חייבות להיות תואמות, לדוגמה `v0.1.0` ו-`0.1.0`
-- חבילת השורש `internationalizer` תלויה בחבילות פלטפורמה כגון `internationalizer-darwin-arm64`
+- תגיות Git וגרסאות חבילת ה-npm חייבות להיות תואמות, לדוגמה `v0.1.0` ו-`0.1.0`
+- חבילת השורש `internationalizer` תלויה בחבילות ייעודיות לפלטפורמות השונות, כגון `internationalizer-darwin-arm64`
- יעדי npm נתמכים: macOS arm64/x64, Linux arm64/x64, Windows x64
-- פרסום דרך CI דורש סוד (secret) ב-GitHub בשם `NPM_TOKEN`
-
-## התחלה מהירה
+- פרסום ב-CI דורש סוד ב-GitHub בשם `NPM_TOKEN`
+
+## תחילת עבודה מהירה
1. יצירת קובץ הגדרות בשורש הפרויקט:
@@ -78,7 +77,11 @@ go build -o internationalizer ./cmd/internationalizer
# .internationalizer.yml
source_locale: en
target_locales: [fr, de, es, ja]
-source_path: locales/en.json
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
llm:
provider: gemini
@@ -92,7 +95,7 @@ llm:
export GOOGLE_AI_STUDIO_API_KEY=your-ai-studio-key
```
-3. תצוגה מקדימה של מה שיתורגם:
+3. הצגת תצוגה מקדימה של המחרוזות שיתורגמו:
```bash
internationalizer translate --dry-run
@@ -104,49 +107,77 @@ internationalizer translate --dry-run
internationalizer translate
```
-5. אימות כל השפות (locales):
+5. אימות כל קובצי השפות:
```bash
internationalizer validate
```
-
+
## פקודות
### `translate`
-איתור מפתחות חסרים ותרגומם באמצעות LLM.
+איתור מפתחות חסרים או לא מעודכנים ותרגומם באמצעות LLM.
```bash
internationalizer translate # translate all locales
internationalizer translate -l fr # translate French only
internationalizer translate --dry-run # preview without API calls
+internationalizer translate --adopt-existing # baseline existing translations without API calls
+internationalizer translate --refresh-policy # refresh prompt/style/model-stale entries
internationalizer translate --batch-size 20 # smaller batches
internationalizer translate --concurrency 2 # fewer parallel calls
```
+מצב התרגום מדווח בנפרד על מפתחות חסרים, חוסר עדכון במקור (source-stale), חוסר עדכון במדיניות (policy-stale), ערכים עדכניים וערכים שנערכו ידנית. באופן זה, עריכה ידנית אינה יכולה להסוות שינוי שבוצע במקור או במדיניות. ערכים שהמדיניות שלהם אינה מעודכנת מדווחים, אך מתורגמים מחדש רק בעת שימוש בדגל `--refresh-policy`. ערכים שנערכו ידנית לעולם אינם נדרסים באופן אוטומטי. יש להשתמש בדגל `--adopt-existing` בעת הטמעת המניפסט בפרויקט עם תרגומים קיימים שעברו בדיקה, או בעת אישור מפורש של עריכה ידנית כבסיס ההשוואה החדש.
+
### `validate`
-בדיקת כל קובצי השפות לאיתור מפתחות חסרים, מפתחות מיותרים וחוסר התאמה באינטרפולציה.
+בדיקת כל קובצי השפות מול מאגרי המקור (source bundles). אימות ברירת המחדל בודק כיסוי מבני (אחוז מפתחות היעד הנדרשים שנמצאו), מדווח על מפתחות עודפים כהתראות, ונכשל כאשר נמצאים מפתחות חסרים, חוסר התאמה באינטרפולציה או מבנה שגוי של ICU MessageFormat.
```bash
internationalizer validate # human-readable output
internationalizer validate --json # machine-readable JSON
internationalizer validate -q # exit code only
+internationalizer validate --strict # enforce translation quality rules
+internationalizer validate --require-state # require current manifest provenance
```
+הדגל `--strict` מדווח גם על כיסוי התרגום. ערך טקסטואלי הזהה למקור נחשב כלא מתורגם, אלא אם מילון המונחים כולל במפורש רשומה זהה מקור-יעד עבור הערך המלא; ההגדרה `ignore_case` נשמרת, אך מונח מילון המשובץ בתוך ערך ארוך יותר אינו מקנה פטור. מצב מחמיר (Strict mode) נכשל בעת הימצאות מפתחות עודפים, ערכים הזהים למקור, שינויים במבנה האינטרפולציה/HTML/קוד/קישורי Markdown, חריגות ממילון המונחים או חוסר בצורות ריבוי שהוגדרו.
+
+הדגל `--require-state` מאמת כל קובץ יעד מול `.internationalizer.lock`. האימות נכשל כאשר מפתח אינו מנוהל (untracked), או כאשר רשומת המקור, מדיניות התרגום או גיבוב ערך היעד אינם מעודכנים. ניתן לשלב דגל זה עם `--strict`.
+
+דוחות הפלט לקריאה אנושית ודוחות ה-JSON משתמשים בקודי ממצאים קבועים:
+
+| קוד | משמעות |
+| --- | --- |
+| `missing_key` / `extra_key` | קבוצות המפתחות במקור וביעד אינן תואמות |
+| `blank_translation` | מקור שאינו ריק מכיל ערך יעד ריק במצב מחמיר |
+| `source_identical` | ערך טקסטואלי במצב מחמיר נותר ללא תרגום |
+| `protected_structure_mismatch` | מבנה האינטרפולציה, ה-HTML, הקוד או הקישורים השתנה |
+| `glossary_violation` | לא נמצא מונח יעד מאושר או וריאנט מאושר |
+| `plural_form_missing` | צורת ריבוי שהוגדרה עבור השפה אינה קיימת |
+| `icu_message_syntax` | הודעת ICU במקור או ביעד אינה תקינה מבנית |
+| `icu_argument_mismatch` | שמות, סוגים או סגנונות מעצבים של ארגומנטי ICU אינם תואמים |
+| `icu_selector_mismatch` | בוררים (selectors) אינם תואמים או שקטגוריית הריבוי אינה חוקית בשפת היעד |
+| `untracked` | לא קיימת רשומת מניפסט עבור ערך היעד |
+| `source_stale` | תוכן המקור השתנה לאחר רישום התרגום |
+| `policy_stale` | הפרומפט שנוצר או הגדרות המודל השתנו |
+| `target_modified` | תוכן היעד שונה מרשומת המניפסט |
+
### `detect`
-זיהוי אוטומטי של תשתית ה-i18n והצעת תצורה (configuration).
+זיהוי אוטומטי של מסגרת ה-i18n בפרויקט והצעת תצורה מתאימה.
```bash
internationalizer detect
```
-תומך ב: react-i18next, next-intl, vue-i18n, vanilla JSON, מסמכי markdown.
+תמיכה במסגרות: react-i18next, next-intl, vue-i18n, vanilla JSON, מסמכי markdown.
### `glossary`
-ניהול מונחי מילון לכל שפה הנאכפים במהלך התרגום.
+ניהול מונחי מילון ייעודיים לכל שפה הנאכפים במהלך התרגום.
```bash
internationalizer glossary list --locale fr
@@ -156,15 +187,15 @@ internationalizer glossary remove --locale fr --source "Dashboard"
### `tm`
-ניהול זיכרון תרגום (מטמון JSONL של מחרוזות שתורגמו בעבר).
+ניהול זיכרון התרגום (מטמון JSONL של מחרוזות שתורגמו בעבר).
```bash
internationalizer tm stats # show record counts
internationalizer tm export # dump as JSON
internationalizer tm clear --force # delete all records
```
-
-## מדריך הגדרות (Configuration)
+
+## מדריך הגדרות
```yaml
# .internationalizer.yml
@@ -173,10 +204,23 @@ internationalizer tm clear --force # delete all records
source_locale: en
# Languages to translate into (required)
-target_locales: [fr, de, es, ja, zh-CN, ar]
-
-# Path to the source locale file (required)
-source_path: locales/en.json
+target_locales: [fr, de, es, ja, yue, zh-CN, zh-TW, ar]
+
+# One or more source-to-target mappings (required).
+# {locale} is replaced with each configured target locale.
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
+ - id: docs
+ source: README.md
+ target: docs/i18n/{locale}.md
+ format: markdown
+
+# Backward compatibility: source_path still maps targets to sibling files
+# such as locales/fr.json. Prefer bundles for new projects.
+# source_path: locales/en.json
# LLM provider settings
llm:
@@ -185,7 +229,7 @@ llm:
# Model name defaults by provider:
# anthropic: claude-opus-5
- # openai: gpt-5.6-luna
+ # openai: gpt-5.6-luna (reasoning effort defaults to max)
# gemini: gemini-3.8-flash
# openrouter: deepseek/deepseek-v4-pro-0813
model: gemini-3.8-flash
@@ -196,6 +240,27 @@ llm:
# Base URL for OpenAI-compatible endpoints (optional)
# base_url: https://api.openai.com
+ # OpenAI GPT-5-series Responses API reasoning effort
+ # (default: max for the OpenAI provider)
+ reasoning_effort: max
+
+ # Optional LLM settings for individual target locales. An override using the
+ # global provider inherits unspecified global settings. A different provider
+ # uses that provider's defaults for unspecified settings.
+ locale_overrides:
+ yue:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-CN:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-TW:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+
# Keys per LLM call (default: 40)
batch_size: 40
@@ -210,11 +275,25 @@ glossary_dir: glossary
# Path to translation memory file (default: .internationalizer/tm.jsonl)
tm_path: .internationalizer/tm.jsonl
+
+# Versioned source, policy, target, and provenance state
+# (default: .internationalizer.lock; commit this file)
+manifest_path: .internationalizer.lock
+
+# Optional translation and strict-validation rules
+validation:
+ plural_style: i18next-v4 # generate and validate target-locale plural forms
```
+מזהי שפה חייבים להיות תגיות BCP 47 בעלות מבנה תקין, כגון `fr`, `pt-BR` או `sr-Latn-RS`. שפות יעד בעלות שקילות קנונית נדחות ככפולות, ודריסות של ספקי מודל לפי שפה תואמות לאיות השקול קנונית. בדוגמה שלעיל, שפות ללא דריסה ספציפית — כולל יפנית — יורשות את תצורת Gemini הגלובלית.
+
+ערכי ICU MessageFormat מנותחים באופן מבני. נתמכים ארגומנטים פשוטים, `select`, `plural`, `selectordinal`, `number`, `date` ו-`time`, כולל הודעות מקוננות, היסטים של ריבוי (plural offsets), בוררי מספרים מדויקים ו-`#`. תהליך האימות בודק תחביר, סוגי ארגומנטים וסגנונות מעצבים, היסטים של ריבוי, זהות ענפי בחירה (select branch identity) וקטגוריות ריבוי של CLDR בשפת היעד. פלט מהספק שמפר כללים מבניים אלה נדחה לפני כתיבתו לקובץ שפה או לרשומת זיכרון תרגום.
+
+בעת שימוש ב-`i18next-v4`, משפחות ריבוי מזוהות במקור מורחבות במהלך התרגום לקטגוריות ה-CLDR של שפת היעד. קטגוריה הקיימת ביעד בלבד משתמשת בערך `_other` ממשפחת המקור כתבנית לתרגום. אימות מחמיר מחייב את קיומן של אותן קטגוריות יעד; קטגוריות הקיימות במקור בלבד הן אופציונליות בשפות יעד שאינן משתמשות בהן.
+
## מדריכי סגנון
-מדריכי סגנון הם קובצי Markdown המוזרקים לפרומפט התרגום של ה-LLM. הם שולטים בטון, ברמת הרשמיות, בטיפוגרפיה ובמוסכמות נוספות הספציפיות לשפה.
+מדריכי סגנון הם קובצי Markdown המוזרקים לפרומפט התרגום של ה-LLM. הם קובעים את הטון, רמת הרשמיות, הטיפוגרפיה ומוסכמות ייחודיות נוספות של השפה.
```
style-guides/
@@ -226,15 +305,17 @@ style-guides/
### מוסכמות משותפות (`_conventions.md`)
-הגדרת כללים החלים על כל השפות: תחביר אינטרפולציה, שימור HTML, מוסכמות לסוגי מחרוזות (כפתורים לעומת תוויות לעומת שגיאות) וכו'.
+הגדרת כללים החלים על כל השפות: תחביר אינטרפולציה, שימור תגיות HTML, מוסכמות עבור סוגי מחרוזות (לחצנים לעומת תוויות לעומת שגיאות) וכדומה.
-### מדריכים לכל שפה (`{locale}.md`)
+### מדריכים לפי שפה (`{locale}.md`)
-הגדרת כללים ספציפיים לשפה: משלב רשמיות (tu לעומת vous), פיסוק (מרכאות כפולות, סימני שאלה הפוכים), צורות ריבוי, עיצוב תאריכים/מספרים ומילון מונחים.
+הגדרת כללים ספציפיים לשפה: משלב רשמיות (פנייה בגוף ראשון או שני), סימני פיסוק (מרכאות, סימני שאלה הפוכים), צורות ריבוי, עיצוב תאריכים ומספרים, ומילון מונחים.
-ראו את [`examples/react-app/style-guides/`](examples/react-app/style-guides/) לדוגמה עובדת.
+מדריכי סגנון הם קלט מדיניות קבוע, ולא פלט מיוצר. Internationalizer קורא אותם אך לעולם אינו משכתב אותם. התוכן שלהם מגובב (hashed) בנפרד ממילון המונחים ומחוזה הפרומפט, כך ששינוי בקוד היישום אינו הופך את התרגום ללא-מעודכן. עריכה מכוונת של מדריך סגנון מסמנת את אותה שפה לבדיקת מדיניות; שינוי ניסוח פנימי של הפרומפט אינו גורם לכך, אלא אם גרסת חוזה הפרומפט השתנתה אף היא.
-## פורמט מילון מונחים
+דוגמה מעשית זמינה בנתיב [`examples/react-app/style-guides/`](../../examples/react-app/style-guides/).
+
+## מבנה מילון המונחים
קובצי מילון מונחים הם מערכי JSON המאוחסנים בנתיב `{glossary_dir}/{locale}.json`:
@@ -243,41 +324,45 @@ style-guides/
{
"source": "Dashboard",
"target": "Tableau de bord",
+ "variants": ["Panneau de contrôle"],
+ "enforcement": "error",
"ignore_case": false,
"whole_word": true
}
]
```
-המונחים מוזרקים לפרומפט ה-LLM כטבלת טרמינולוגיה, מה שמבטיח תרגום עקבי של מונחי מפתח ברחבי האפליקציה.
-
-## זיכרון תרגום (Translation Memory)
+השדה `variants` מפרט צורות יעד מאושרות נוספות. השדה `enforcement` יכול להכיל את הערכים `error`, `warning`, או להישאר ריק להתנהגות ברירת מחדל מסוג שגיאה. המונחים מוזרקים לפרומפט ה-LLM כטבלת מונחים, מה שמבטיח תרגום עקבי ברחבי היישום שלכם. רשומה מדויקת כגון `{"source":"API","target":"API"}` מעניקה פטור לאותו ערך שלם הזהה למקור מממצאי ערך לא-מתורגם במצב מחמיר; היא אינה מעניקה פטור לערך ארוך יותר שרק מכיל בתוכו את המונח `API`.
+
+## זיכרון תרגום
-זיכרון התרגום מאוחסן כקובץ JSONL (רשומת JSON אחת בכל שורה). כל רשומה מכילה:
+זיכרון התרגום נשמר כקובץ JSONL (רשומת JSON אחת בכל שורה). כל רשומה כוללת:
-- את מפתח המקור והערך
-- את הערך המתורגם
-- גיבוב (hash) מסוג SHA-256 של ערך המקור
-- חותמת זמן (timestamp)
-
-בהרצות עוקבות, מחרוזות שלא השתנו מוגשות ממטמון ה-TM ללא קריאה ל-LLM, מה שחוסך זמן ועלויות API. קובץ ה-TM ידידותי ל-Git וניתן לדחוף אותו (commit) יחד עם קובצי השפות.
+- את המאגר (bundle), המפתח, ערך המקור, הערך המתורגם ושפת היעד הקנונית
+- גיבובי מקור, מדריך סגנון, מילון מונחים, חוזה פרומפט ומדיניות משולבת
+- את הספק והמודל שהפיקו את התרגום
+- חותמת זמן
+בהרצות עוקבות, מחרוזות בעלות אותם גיבובי מקור ומדיניות מוגשות מתוך המטמון ללא קריאה ל-LLM. נתיב ברירת המחדל נמצא תחת התיקייה המוחרגת `.internationalizer/`, כך שהוא נשמר כמטמון מקומי. ניתן להגדיר את `tm_path` לנתיב המנוהל בגיט אם הפרויקט משתף במכוון את זיכרון התרגום. מניפסט המעקב `.internationalizer.lock` מנוהל בגרסאות בנפרד.
+
## פורמטים נתמכים
-| פורמט | סיומות | מצב |
-|--------|-----------|------|
-| JSON | `.json` | מפתח-ערך (מקונן, שטוח בשיטת נקודה) |
+| פורמט | סיומות | מצב |
+|---|---|---|
+| JSON | `.json` | מפתח-ערך (מקונן, משוטח בייצוג נקודה) |
| YAML | `.yml`, `.yaml` | מפתח-ערך (משמר הערות וסדר) |
-| Markdown | `.md`, `.mdx` | תרגום מסמך שלם |
+| Markdown | `.md`, `.mdx` | פתיח ומקטעים ברמת H2 |
-## זיהוי סוג פרויקט
+קובצי יעד מסוג Markdown מכילים הערות `internationalizer:unit` נסתרות לפני מקטעי H2. סמנים יציבים אלה מאפשרים ל-Internationalizer להוסיף, להזיז או לערוך מקטע מקור יחיד מבלי לתרגם מחדש מקטעים שאינם קשורים. מסמכים קיימים ללא סמנים יקבלו סמנים אלה בעדכון המוצלח הבא שלהם.
+
+## זיהוי סוג הפרויקט
-הפקודה `internationalizer detect` מזהה את תצורת ה-i18n שלכם על ידי בדיקת:
+הפקודה `internationalizer detect` מזהה את הגדרות ה-i18n שלכם על ידי בדיקת:
- תלויות ב-`package.json` עבור react-i18next, next-intl או vue-i18n
-- מבני ספריות התואמים לתבניות שפה (locale) נפוצות
+- מבני תיקיות התואמים לדפוסי שפות (locales) נפוצים
- סיומות קבצים ומוסכמות שמות
-
+
## ארכיטקטורה
```
@@ -292,30 +377,35 @@ internal/
openai.go OpenAI / compatible backend
gemini.go Google Gemini via AI Studio backend
OpenRouter uses openai.go with custom base_url
+ locale/ BCP 47 identity and CLDR plural categories
+ message/ ICU MessageFormat parser and structural comparison
+ policy/ Stable translation-policy hashing
+ state/ Versioned translation manifest
styleguide/ Style guide loader
tm/ JSONL translation memory
translate/ Translation orchestrator
validate/ Locale validation and diffing
```
-
+
## השוואה לחלופות
-| תכונה | Internationalizer | i18next | Crowdin | LLM גנרי |
-|---------|------------------|---------|---------|-------------|
+| תכונה | Internationalizer | i18next | Crowdin | LLM כללי |
+|---|---|---|---|---|
| תרגום מבוסס LLM | כן | לא | חלקי | כן |
-| מדריכי סגנון לכל שפה | כן | לא | לא | לא |
+| מדריכי סגנון ייעודיים לכל שפה | כן | לא | לא | לא |
| אכיפת מילון מונחים | כן | לא | כן | לא |
| זיכרון תרגום | כן | לא | כן | לא |
| CLI / הרצה מקומית | כן | לא רלוונטי | לא | ידני |
-| קבצים ידידותיים ל-Git | כן | כן | חלקי | ידני |
+| קבצים מותאמים ל-Git | כן | כן | חלקי | ידני |
| ללא תלות ב-SaaS | כן | כן | לא | משתנה |
| קוד פתוח (AGPL-3.0) | כן | כן | לא | משתנה |
-
+
## רישיון
-[AGPL-3.0](LICENSE)
-
-## תרומה לקוד (Contributing)
+[AGPL-3.0](../../LICENSE)
-ראו את [CONTRIBUTING.md](CONTRIBUTING.md) להנחיות והגדרות פיתוח. כל התרומות דורשות אישור DCO.
+למידע על תלויות, ראו את [THIRD_PARTY_NOTICES.md](../../THIRD_PARTY_NOTICES.md).
+
+## תרומה לפרויקט
+להנחיות ולהגדרת סביבת הפיתוח, ראו את [CONTRIBUTING.md](../../CONTRIBUTING.md). כל התרומות דורשות אישור DCO.
diff --git a/docs/i18n/hi.md b/docs/i18n/hi.md
index cd0a363..c725b57 100644
--- a/docs/i18n/hi.md
+++ b/docs/i18n/hi.md
@@ -9,24 +9,32 @@
सॉफ़्टवेयर प्रोजेक्ट्स के लिए AI-नेटिव इंटरनेशनलाइज़ेशन पाइपलाइन। LLMs का उपयोग करके i18n फ़ाइलों का अनुवाद, सत्यापन और प्रबंधन करें।
[](https://github.com/Tom-R-Main/Internationalizer/actions/workflows/ci.yml)
-[](LICENSE)
+[](../../LICENSE)
+
+العربية · বাংলা · Čeština · Dansk · Deutsch · Ελληνικά · Español · Suomi · Français · עברית · हिन्दी · Indonesia · Italiano · 日本語 · 한국어 · Bahasa Melayu
Nederlands · ਪੰਜਾਬੀ · Polski · Português · Română · Русский · Svenska · తెలుగు · ไทย · Türkçe · Українська · Tiếng Việt · 粵語 · 简体中文 · 繁體中文
+
+
+---
+
+
## Internationalizer क्यों?
-ज़्यादातर i18n टूल्स या तो रनटाइम लाइब्रेरीज़ (i18next, react-intl) हैं या की-मैनेजमेंट SaaS प्लेटफ़ॉर्म (Crowdin, Lokalise) हैं। इनमें से कोई भी अनुवाद की वास्तविक समस्या को अच्छी तरह से हल नहीं करता है:
+अधिकांश i18n टूल्स या तो रनटाइम लाइब्रेरीज़ (i18next, react-intl) होते हैं या की-मैनेजमेंट SaaS प्लेटफ़ॉर्म (Crowdin, Lokalise)। इनमें से कोई भी अनुवाद की वास्तविक समस्या का प्रभावी समाधान नहीं करता है:
-- **मैन्युअल अनुवाद** कुछ भाषाओं से आगे स्केल नहीं हो पाता है
-- **मशीन ट्रांसलेशन APIs** (Google Translate, DeepL) आपकी शब्दावली, टोन और UI नियमों को नज़रअंदाज़ कर देते हैं
-- **सामान्य LLM अनुवाद** बेहतर काम करता है, लेकिन ग्लॉसरी और स्टाइल गाइड के बिना, आपको असंगत परिणाम मिलते हैं
+- **मैन्युअल अनुवाद** कुछ भाषाओं से आगे स्केल नहीं हो पाता
+- **मशीन ट्रांसलेशन APIs** (Google Translate, DeepL) आपकी शब्दावली, टोन और UI नियमों की अनदेखी करते हैं
+- **सामान्य LLM अनुवाद** बेहतर काम करता है, लेकिन ग्लॉसरी और स्टाइल गाइड के बिना असंगत परिणाम देता है
Internationalizer अलग है। यह एक **CLI पाइपलाइन** है जो LLM अनुवाद को इनके साथ जोड़ती है:
-- **प्रति-भाषा ग्लॉसरी (Per-language glossaries)** — आपके पूरे ऐप में एक समान शब्दावली लागू करती है
-- **प्रति-भाषा स्टाइल गाइड** — टोन, औपचारिकता, बहुवचन (pluralization) और टाइपोग्राफी को नियंत्रित करती है
-- **ट्रांसलेशन मेमोरी** — बिना बदले गए स्ट्रिंग्स को छोड़ें, API कॉल्स पर पैसे बचाएं
-- **की वैलिडेशन (Key validation)** — शिप करने से पहले छूटे हुए अनुवादों और इंटरपोलेशन मिसमैच को पकड़ें
+- **प्रति-भाषा ग्लॉसरी** — आपके पूरे ऐप में एक समान शब्दावली लागू करें
+- **प्रति-भाषा स्टाइल गाइड्स** — टोन, औपचारिकता, बहुवचन (pluralization) और टाइपोग्राफी को नियंत्रित करें
+- **ट्रांसलेशन मेमोरी** — अपरिवर्तित स्ट्रिंग्स को छोड़ें और API कॉल्स के खर्च बचाएँ
+- **डिटरमिनिस्टिक वैलिडेशन** — शिप करने से पहले अनुपलब्ध या अतिरिक्त कीज़, सुरक्षित संरचना के विचलन, ग्लॉसरी संबंधी समस्याओं और बहुवचन या ICU त्रुटियों को पकड़ें
-## इंस्टॉलेशन (Installation)
+
+## इंस्टॉलेशन
npm से इंस्टॉल करें:
@@ -34,13 +42,13 @@ npm से इंस्टॉल करें:
npm install -g internationalizer
```
-या ग्लोबल इंस्टॉल के बिना रन करें:
+या बिना ग्लोबल इंस्टॉल के रन करें:
```bash
npx internationalizer --help
```
-npm पैकेज प्लेटफ़ॉर्म-विशिष्ट वैकल्पिक निर्भरताओं (optional dependencies) के माध्यम से npm से मैचिंग प्रीबिल्ट बाइनरी इंस्टॉल करता है।
+npm पैकेज प्लेटफ़ॉर्म-विशिष्ट वैकल्पिक निर्भरताओं (optional dependencies) के ज़रिए npm से संगत प्रीबिल्ट बाइनरी इंस्टॉल करता है।
Go के साथ इंस्टॉल करें:
@@ -56,22 +64,28 @@ cd Internationalizer
go build -o internationalizer ./cmd/internationalizer
```
+
## npm पैकेजेस
-- Git टैग्स और npm पैकेज वर्ज़न मैच होने चाहिए, उदाहरण के लिए `v0.1.0` और `0.1.0`
-- रूट `internationalizer` पैकेज प्लेटफ़ॉर्म पैकेजेस पर निर्भर करता है जैसे कि `internationalizer-darwin-arm64`
+- Git टैग्स और npm पैकेज वर्ज़न मेल खाने चाहिए, उदाहरण के लिए `v0.1.0` और `0.1.0`
+- रूट `internationalizer` पैकेज `internationalizer-darwin-arm64` जैसे प्लेटफ़ॉर्म पैकेजेस पर निर्भर करता है
- समर्थित npm टार्गेट्स: macOS arm64/x64, Linux arm64/x64, Windows x64
- CI पब्लिशिंग के लिए `NPM_TOKEN` नाम के GitHub सीक्रेट की आवश्यकता होती है
+
## क्विक स्टार्ट
-1. अपने प्रोजेक्ट रूट में एक कॉन्फ़िग फ़ाइल बनाएँ:
+1. अपने प्रोजेक्ट रूट में एक कॉन्फ़िगरेशन फ़ाइल बनाएँ:
```yaml
# .internationalizer.yml
source_locale: en
target_locales: [fr, de, es, ja]
-source_path: locales/en.json
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
llm:
provider: gemini
@@ -85,7 +99,7 @@ llm:
export GOOGLE_AI_STUDIO_API_KEY=your-ai-studio-key
```
-3. प्रीव्यू करें कि क्या अनुवाद किया जाएगा:
+3. अनुवाद होने वाली सामग्री का प्रीव्यू देखें:
```bash
internationalizer translate --dry-run
@@ -97,49 +111,78 @@ internationalizer translate --dry-run
internationalizer translate
```
-5. सभी लोकेल्स (locales) को वैलिडेट करें:
+5. सभी लोकेल्स को वैलिडेट करें:
```bash
internationalizer validate
```
+
## कमांड्स
### `translate`
-मिसिंग कीज़ (keys) खोजें और LLM के माध्यम से उनका अनुवाद करें।
+अनुपलब्ध या पुरानी (stale) कीज़ ढूँढें और LLM के माध्यम से उनका अनुवाद करें।
```bash
-internationalizer translate # सभी लोकेल्स का अनुवाद करें
-internationalizer translate -l fr # केवल फ्रेंच का अनुवाद करें
-internationalizer translate --dry-run # API कॉल्स के बिना प्रीव्यू करें
-internationalizer translate --batch-size 20 # छोटे बैचेस
-internationalizer translate --concurrency 2 # कम पैरेलल कॉल्स
+internationalizer translate # translate all locales
+internationalizer translate -l fr # translate French only
+internationalizer translate --dry-run # preview without API calls
+internationalizer translate --adopt-existing # baseline existing translations without API calls
+internationalizer translate --refresh-policy # refresh prompt/style/model-stale entries
+internationalizer translate --batch-size 20 # smaller batches
+internationalizer translate --concurrency 2 # fewer parallel calls
```
+ट्रांसलेशन स्टेट अनुपलब्ध (missing), स्रोत-पुरानी (source-stale), नीति-पुरानी (policy-stale), वर्तमान (current) और मैन्युअल रूप से संपादित (manually edited) स्थितियों की स्वतंत्र रूप से रिपोर्ट करती है, ताकि कोई मैन्युअल संपादन किसी स्रोत या नीति परिवर्तन को छिपा न सके। नीति-पुरानी वैल्यूज़ रिपोर्ट की जाती हैं, लेकिन केवल `--refresh-policy` के साथ ही उनका पुनः अनुवाद किया जाता है। मैन्युअल रूप से संपादित वैल्यूज़ कभी भी स्वचालित रूप से अधिलेखित (overwrite) नहीं की जाती हैं। समीक्षित अनुवादों में मेनिफ़ेस्ट को शामिल करते समय या किसी समीक्षित मैन्युअल संपादन को स्पष्ट रूप से नई आधार रेखा (baseline) के रूप में स्वीकार करते समय `--adopt-existing` का उपयोग करें।
+
### `validate`
-मिसिंग कीज़, अतिरिक्त कीज़ और इंटरपोलेशन मिसमैच के लिए सभी लोकेल फ़ाइलों की जाँच करें।
+सभी लोकेल फ़ाइलों की उनके स्रोत बंडलों से जाँच करें। डिफ़ॉल्ट वैलिडेशन संरचनात्मक कवरेज (उपस्थित आवश्यक टार्गेट कीज़ का प्रतिशत) की जाँच करता है, अतिरिक्त कीज़ को चेतावनियों के रूप में रिपोर्ट करता है, और अनुपलब्ध कीज़, इंटरपोलेशन मिसमैच या अमान्य ICU MessageFormat संरचना पर विफल हो जाता है।
```bash
-internationalizer validate # ह्यूमन-रीडेबल आउटपुट
-internationalizer validate --json # मशीन-रीडेबल JSON
-internationalizer validate -q # केवल एग्ज़िट कोड
+internationalizer validate # human-readable output
+internationalizer validate --json # machine-readable JSON
+internationalizer validate -q # exit code only
+internationalizer validate --strict # enforce translation quality rules
+internationalizer validate --require-state # require current manifest provenance
```
+`--strict` अनुवादित कवरेज की भी रिपोर्ट करता है। अपने स्रोत के समान भाषाई मान तब तक अनुवादित नहीं माना जाता जब तक कि ग्लॉसरी में संपूर्ण मान के लिए स्पष्ट रूप से समान-स्रोत, समान-टार्गेट प्रविष्टि मौजूद न हो; `ignore_case` का पालन किया जाता है, लेकिन किसी लंबे मान में अंतर्निहित ग्लॉसरी शब्द छूट के दायरे में नहीं आता। स्ट्रिक्ट मोड अतिरिक्त कीज़, स्रोत-समान मानों, परिवर्तित इंटरपोलेशन/HTML/कोड/Markdown-लिंक संरचना, ग्लॉसरी उल्लंघनों और कॉन्फ़िगर किए गए बहुवचन रूपों पर विफल होता है।
+
+`--require-state` प्रत्येक टार्गेट को `.internationalizer.lock` के सापेक्ष सत्यापित करता है। जब कोई की अनट्रैक्ड होती है, या उसका रिकॉर्ड किया गया स्रोत, अनुवाद नीति, या टार्गेट हैश पुराना होता है, तो यह विफल हो जाता है। इसे `--strict` के साथ जोड़ा जा सकता है।
+
+मानव-पठनीय और JSON रिपोर्ट स्थिर कोड्स का उपयोग करती हैं:
+
+| कोड | अर्थ |
+| --- | --- |
+| `missing_key` / `extra_key` | स्रोत और टार्गेट कीज़ के सेट भिन्न हैं |
+| `blank_translation` | गैर-रिक्त स्रोत का स्ट्रिक्ट-मोड टार्गेट रिक्त है |
+| `source_identical` | स्ट्रिक्ट-मोड भाषाई मान अनुवादित नहीं हुआ है |
+| `protected_structure_mismatch` | इंटरपोलेशन, HTML, कोड, या लिंक संरचना में परिवर्तन हुआ है |
+| `glossary_violation` | कोई स्वीकृत टार्गेट शब्द या रूप नहीं मिला |
+| `plural_form_missing` | कॉन्फ़िगर किया गया लोकेल बहुवचन रूप अनुपस्थित है |
+| `icu_message_syntax` | स्रोत या टार्गेट ICU संदेश विकृत है |
+| `icu_argument_mismatch` | ICU आर्गुमेंट के नाम, प्रकार या फ़ॉर्मेटर शैलियाँ भिन्न हैं |
+| `icu_selector_mismatch` | चयनकर्ता भिन्न हैं या टार्गेट लोकेल के लिए बहुवचन श्रेणी अमान्य है |
+| `untracked` | टार्गेट के लिए कोई मेनिफ़ेस्ट रिकॉर्ड मौजूद नहीं है |
+| `source_stale` | रिकॉर्ड किए गए अनुवाद के बाद स्रोत सामग्री बदल गई |
+| `policy_stale` | जनरेट किया गया प्रॉम्प्ट या मॉडल सेटिंग्स बदल गईं |
+| `target_modified` | टार्गेट सामग्री मेनिफ़ेस्ट रिकॉर्ड से भिन्न है |
+
### `detect`
-i18n फ़्रेमवर्क का स्वतः पता लगाएँ (Auto-detect) और कॉन्फ़िगरेशन का सुझाव दें।
+i18n फ़्रेमवर्क का स्वतः पता लगाएँ और कॉन्फ़िगरेशन का सुझाव दें।
```bash
internationalizer detect
```
-समर्थित: react-i18next, next-intl, vue-i18n, vanilla JSON, markdown docs।
+समर्थन: react-i18next, next-intl, vue-i18n, vanilla JSON, markdown docs।
### `glossary`
-प्रति-भाषा ग्लॉसरी शब्दों को प्रबंधित करें जिन्हें अनुवाद के दौरान लागू किया जाता है।
+प्रति-भाषा ग्लॉसरी शब्दों का प्रबंधन करें जिन्हें अनुवाद के दौरान लागू किया जाता है।
```bash
internationalizer glossary list --locale fr
@@ -152,163 +195,232 @@ internationalizer glossary remove --locale fr --source "Dashboard"
ट्रांसलेशन मेमोरी (पहले अनुवादित स्ट्रिंग्स का JSONL कैश) प्रबंधित करें।
```bash
-internationalizer tm stats # रिकॉर्ड काउंट्स दिखाएं
-internationalizer tm export # JSON के रूप में डंप करें
-internationalizer tm clear --force # सभी रिकॉर्ड्स हटाएं
+internationalizer tm stats # show record counts
+internationalizer tm export # dump as JSON
+internationalizer tm clear --force # delete all records
```
-## कॉन्फ़िगरेशन संदर्भ (Configuration Reference)
+
+## कॉन्फ़िगरेशन संदर्भ
```yaml
# .internationalizer.yml
-# स्रोत भाषा (डिफ़ॉल्ट: en)
+# Source language (default: en)
source_locale: en
-# जिन भाषाओं में अनुवाद करना है (आवश्यक)
-target_locales: [fr, de, es, ja, zh-CN, ar]
-
-# स्रोत लोकेल फ़ाइल का पाथ (आवश्यक)
-source_path: locales/en.json
-
-# LLM प्रोवाइडर सेटिंग्स
+# Languages to translate into (required)
+target_locales: [fr, de, es, ja, yue, zh-CN, zh-TW, ar]
+
+# One or more source-to-target mappings (required).
+# {locale} is replaced with each configured target locale.
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
+ - id: docs
+ source: README.md
+ target: docs/i18n/{locale}.md
+ format: markdown
+
+# Backward compatibility: source_path still maps targets to sibling files
+# such as locales/fr.json. Prefer bundles for new projects.
+# source_path: locales/en.json
+
+# LLM provider settings
llm:
- # प्रोवाइडर: "anthropic", "openai", "gemini", या "openrouter" (डिफ़ॉल्ट: gemini)
+ # Provider: "anthropic", "openai", "gemini", or "openrouter" (default: gemini)
provider: gemini
- # प्रोवाइडर के अनुसार डिफ़ॉल्ट मॉडल नाम:
+ # Model name defaults by provider:
# anthropic: claude-opus-5
- # openai: gpt-5.6-luna
+ # openai: gpt-5.6-luna (reasoning effort defaults to max)
# gemini: gemini-3.8-flash
# openrouter: deepseek/deepseek-v4-pro-0813
model: gemini-3.8-flash
- # API की (key) वाला एनवायरनमेंट वेरिएबल
+ # Environment variable containing the API key
api_key_env: GOOGLE_AI_STUDIO_API_KEY
- # OpenAI-कम्पैटिबल एंडपॉइंट्स के लिए बेस URL (वैकल्पिक)
+ # Base URL for OpenAI-compatible endpoints (optional)
# base_url: https://api.openai.com
-# प्रति LLM कॉल कीज़ (डिफ़ॉल्ट: 40)
+ # OpenAI GPT-5-series Responses API reasoning effort
+ # (default: max for the OpenAI provider)
+ reasoning_effort: max
+
+ # Optional LLM settings for individual target locales. An override using the
+ # global provider inherits unspecified global settings. A different provider
+ # uses that provider's defaults for unspecified settings.
+ locale_overrides:
+ yue:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-CN:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-TW:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+
+# Keys per LLM call (default: 40)
batch_size: 40
-# पैरेलल LLM कॉल्स (डिफ़ॉल्ट: 4)
+# Parallel LLM calls (default: 4)
concurrency: 4
-# प्रति-लोकेल स्टाइल गाइड मार्कडाउन फ़ाइलों वाली डायरेक्टरी (डिफ़ॉल्ट: style-guides)
+# Directory containing per-locale style guide Markdown files (default: style-guides)
style_guides_dir: style-guides
-# प्रति-लोकेल ग्लॉसरी JSON फ़ाइलों वाली डायरेक्टरी (डिफ़ॉल्ट: glossary)
+# Directory containing per-locale glossary JSON files (default: glossary)
glossary_dir: glossary
-# ट्रांसलेशन मेमोरी फ़ाइल का पाथ (डिफ़ॉल्ट: .internationalizer/tm.jsonl)
+# Path to translation memory file (default: .internationalizer/tm.jsonl)
tm_path: .internationalizer/tm.jsonl
+
+# Versioned source, policy, target, and provenance state
+# (default: .internationalizer.lock; commit this file)
+manifest_path: .internationalizer.lock
+
+# Optional translation and strict-validation rules
+validation:
+ plural_style: i18next-v4 # generate and validate target-locale plural forms
```
+लोकेल आइडेंटिफ़ायर सही रूप से गठित BCP 47 टैग होने चाहिए जैसे `fr`, `pt-BR`, या `sr-Latn-RS`। विहित-समतुल्य (canonical-equivalent) टार्गेट लोकेल्स को डुप्लिकेट के रूप में अस्वीकार कर दिया जाता है, और लोकेल-विशिष्ट प्रोवाइडर ओवरराइड्स विहित-समतुल्य वर्तनी से मेल खाते हैं। ऊपर दिए गए उदाहरण में, जापानी सहित बिना ओवरराइड वाले लोकेल्स ग्लोबल Gemini कॉन्फ़िगरेशन इनहेरिट करते हैं।
+
+ICU MessageFormat मानों को संरचनात्मक रूप से पार्स किया जाता है। सामान्य आर्गुमेंट्स, `select`, `plural`, `selectordinal`, `number`, `date`, और `time` समर्थित हैं, जिनमें नेस्टेड संदेश, प्लुरल ऑफ़सेट्स, सटीक-संख्या चयनकर्ता और `#` शामिल हैं। वैलिडेशन सिंटैक्स, आर्गुमेंट प्रकारों और फ़ॉर्मेटर शैलियों, प्लुरल ऑफ़सेट्स, सेलेक्ट शाखा पहचान, और टार्गेट लोकेल की CLDR बहुवचन श्रेणियों की जाँच करता है। इन अपरिवर्तनीयों (invariants) को तोड़ने वाले प्रोवाइडर आउटपुट को लोकेल फ़ाइल या ट्रांसलेशन-मेमोरी रिकॉर्ड लिखे जाने से पहले ही अस्वीकार कर दिया जाता है।
+
+`i18next-v4` के साथ, पहचानी गई स्रोत बहुवचन फ़ैमिलीज़ का अनुवाद के दौरान टार्गेट लोकेल की CLDR श्रेणियों में विस्तार किया जाता है। केवल-टार्गेट श्रेणी स्रोत फ़ैमिली के `_other` मान को अपने अनुवाद टेम्पलेट के रूप में उपयोग करती है। स्ट्रिक्ट वैलिडेशन के लिए उन टार्गेट श्रेणियों की आवश्यकता होती है; केवल-स्रोत श्रेणियाँ उन टार्गेट लोकेल्स के लिए वैकल्पिक हैं जो उनका उपयोग नहीं करते हैं।
+
+
## स्टाइल गाइड्स
-स्टाइल गाइड्स मार्कडाउन फ़ाइलें हैं जिन्हें LLM ट्रांसलेशन प्रॉम्प्ट में इंजेक्ट किया जाता है। वे टोन, औपचारिकता, टाइपोग्राफी और अन्य भाषा-विशिष्ट नियमों को नियंत्रित करते हैं।
+स्टाइल गाइड्स मार्कडाउन फ़ाइलें होती हैं जिन्हें LLM अनुवाद प्रॉम्प्ट में इंजेक्ट किया जाता है। वे टोन, औपचारिकता, टाइपोग्राफी और भाषा-विशिष्ट अन्य नियमों को नियंत्रित करती हैं।
```
style-guides/
- _conventions.md # सभी भाषाओं के लिए साझा नियम
- fr.md # फ्रेंच-विशिष्ट नियम
- ja.md # जापानी-विशिष्ट नियम
- ar.md # अरबी-विशिष्ट नियम
+ _conventions.md # shared rules for all languages
+ fr.md # French-specific rules
+ ja.md # Japanese-specific rules
+ ar.md # Arabic-specific rules
```
-### साझा नियम (`_conventions.md`)
+### साझा परिपाटियाँ (`_conventions.md`)
-उन नियमों को परिभाषित करें जो सभी भाषाओं पर लागू होते हैं: इंटरपोलेशन सिंटैक्स, HTML संरक्षण, स्ट्रिंग प्रकार के नियम (बटन बनाम लेबल बनाम त्रुटियां), आदि।
+सभी भाषाओं पर लागू होने वाले नियमों को परिभाषित करें: इंटरपोलेशन सिंटैक्स, HTML संरक्षण, स्ट्रिंग प्रकार के नियम (बटन बनाम लेबल बनाम त्रुटियाँ), आदि।
### प्रति-भाषा गाइड्स (`{locale}.md`)
-भाषा-विशिष्ट नियमों को परिभाषित करें: औपचारिकता रजिस्टर (tu बनाम vous), विराम चिह्न (guillemets, उल्टे प्रश्न चिह्न), बहुवचन रूप, दिनांक/संख्या स्वरूपण, और एक शब्दावली ग्लॉसरी।
+भाषा-विशिष्ट नियमों को परिभाषित करें: औपचारिकता रजिस्टर (tu बनाम vous), विराम चिह्न (guillemets, उल्टे प्रश्न चिह्न), बहुवचन रूप, दिनांक/संख्या फ़ॉर्मेटिंग और शब्दावली ग्लॉसरी।
-काम करने वाले उदाहरण के लिए [`examples/react-app/style-guides/`](examples/react-app/style-guides/) देखें。
+स्टाइल गाइड्स स्थायी नीति इनपुट हैं, न कि जनरेट किया गया आउटपुट। Internationalizer उन्हें पढ़ता है लेकिन कभी दोबारा नहीं लिखता। उनकी सामग्री को ग्लॉसरी और प्रॉम्प्ट अनुबंध से अलग हैश किया जाता है, ताकि किसी एप्लिकेशन कोड परिवर्तन से कोई अनुवाद पुराना न हो। किसी गाइड को संपादित करने से जानबूझकर वह लोकेल नीति समीक्षा के लिए चिह्नित हो जाता है; आंतरिक प्रॉम्प्ट शब्दावली बदलने से ऐसा तब तक नहीं होता, जब तक कि प्रॉम्प्ट अनुबंध संस्करण भी न बदले।
+कार्यशील उदाहरण के लिए [`examples/react-app/style-guides/`](../../examples/react-app/style-guides/) देखें।
+
+
## ग्लॉसरी फ़ॉर्मेट
-ग्लॉसरी फ़ाइलें `{glossary_dir}/{locale}.json` में स्टोर किए गए JSON ऐरे (arrays) हैं:
+ग्लॉसरी फ़ाइलें `{glossary_dir}/{locale}.json` में संग्रहीत JSON ऐरे होती हैं:
```json
[
{
"source": "Dashboard",
"target": "Tableau de bord",
+ "variants": ["Panneau de contrôle"],
+ "enforcement": "error",
"ignore_case": false,
"whole_word": true
}
]
```
-शब्दों को LLM प्रॉम्प्ट में एक शब्दावली तालिका (terminology table) के रूप में इंजेक्ट किया जाता है, जो आपके एप्लिकेशन में प्रमुख शब्दों का एक समान अनुवाद सुनिश्चित करता है।
+`variants` अन्य अनुमोदित टार्गेट रूपों को सूचीबद्ध करता है। `enforcement` का मान `error`, `warning` हो सकता है, या डिफ़ॉल्ट error व्यवहार के लिए इसे छोड़ा जा सकता है। शब्दों को LLM प्रॉम्प्ट में शब्दावली तालिका के रूप में इंजेक्ट किया जाता है, जिससे आपके एप्लिकेशन में सुसंगत अनुवाद सुनिश्चित होता है। `{"source":"API","target":"API"}` जैसी सटीक प्रविष्टि उस संपूर्ण स्रोत-समान मान को स्ट्रिक्ट गैर-अनुवादित निष्कर्षों से छूट भी प्रदान करती है; यह केवल `API` युक्त किसी लंबे मान को छूट नहीं देती।
+
## ट्रांसलेशन मेमोरी
-ट्रांसलेशन मेमोरी को JSONL फ़ाइल (प्रति पंक्ति एक JSON रिकॉर्ड) के रूप में स्टोर किया जाता है। प्रत्येक रिकॉर्ड में शामिल हैं:
+ट्रांसलेशन मेमोरी JSONL फ़ाइल (प्रति पंक्ति एक JSON रिकॉर्ड) के रूप में संग्रहीत होती है। प्रत्येक रिकॉर्ड में शामिल होते हैं:
-- स्रोत की (key) और वैल्यू
-- अनुवादित वैल्यू
-- स्रोत वैल्यू का SHA-256 हैश
+- बंडल, की, स्रोत मान, अनुवादित मान, और विहित टार्गेट लोकेल
+- स्रोत, स्टाइल-गाइड, ग्लॉसरी, प्रॉम्प्ट-अनुबंध और संयुक्त नीति हैश
+- अनुवाद उत्पन्न करने वाला प्रोवाइडर और मॉडल
- एक टाइमस्टैम्प
-बाद के रन पर, बिना बदले गए स्ट्रिंग्स को LLM को कॉल किए बिना TM कैश से सर्व किया जाता है, जिससे समय और API लागत दोनों की बचत होती है। TM फ़ाइल git-फ़्रेंडली है और इसे आपकी लोकेल फ़ाइलों के साथ कमिट किया जा सकता है।
+बाद के रन में, समान स्रोत और नीति हैश वाले स्ट्रिंग्स LLM को कॉल किए बिना कैश से प्रदान किए जाते हैं। डिफ़ॉल्ट पाथ अनदेखी की जाने वाली `.internationalizer/` डायरेक्टरी के अंतर्गत होता है, इसलिए यह एक स्थानीय कैश बना रहता है। यदि आपका प्रोजेक्ट जानबूझकर ट्रांसलेशन मेमोरी साझा करता है, तो `tm_path` को किसी ट्रैक्ड स्थान पर सेट करें। समीक्षा योग्य `.internationalizer.lock` मेनिफ़ेस्ट को अलग से वर्ज़न किया जाता है।
-## समर्थित फ़ॉर्मेट्स
+
+## समर्थित फ़ॉर्मेट
-| फ़ॉर्मेट | एक्सटेंशन्स | मोड |
+| फ़ॉर्मेट | एक्सटेंशन | मोड |
|--------|-----------|------|
| JSON | `.json` | की-वैल्यू (नेस्टेड, डॉट-नोटेशन फ़्लैटन्ड) |
| YAML | `.yml`, `.yaml` | की-वैल्यू (टिप्पणियों और क्रम को सुरक्षित रखता है) |
-| Markdown | `.md`, `.mdx` | संपूर्ण-दस्तावेज़ अनुवाद |
+| Markdown | `.md`, `.mdx` | प्रस्तावना और H2-स्तरीय अनुभाग |
+
+Markdown टार्गेट्स में H2 अनुभागों से पहले अदृश्य `internationalizer:unit` टिप्पणियाँ होती हैं। ये स्थिर मार्कर Internationalizer को असंबंधित अनुभागों का पुनः अनुवाद किए बिना किसी एक स्रोत अनुभाग को जोड़ने, स्थानांतरित करने या संपादित करने की सुविधा देते हैं। मौजूदा बिना मार्कर वाले दस्तावेज़ों को उनके अगले सफल अपडेट पर मार्कर प्राप्त होते हैं।
-## प्रोजेक्ट टाइप डिटेक्शन
+
+## प्रोजेक्ट प्रकार की पहचान
`internationalizer detect` इनकी जाँच करके आपके i18n सेटअप की पहचान करता है:
- react-i18next, next-intl, या vue-i18n के लिए `package.json` डिपेंडेंसीज़
-- सामान्य लोकेल पैटर्न से मेल खाने वाले डायरेक्टरी स्ट्रक्चर्स
-- फ़ाइल एक्सटेंशन और नेमिंग कन्वेंशन्स
+- सामान्य लोकेल पैटर्न से मेल खाने वाली डायरेक्टरी संरचनाएँ
+- फ़ाइल एक्सटेंशन और नामकरण परिपाटियाँ
+
## आर्किटेक्चर
```
-cmd/internationalizer/ CLI एंट्री पॉइंट और कमांड डेफिनिशन्स
+cmd/internationalizer/ CLI entry point and command definitions
internal/
- config/ डिफ़ॉल्ट्स के साथ YAML कॉन्फ़िग लोडिंग
- detect/ प्रोजेक्ट टाइप ऑटो-डिटेक्शन
- formats/ फ़ॉर्मेट पार्सर्स (JSON, YAML, Markdown)
- glossary/ प्रति-लोकेल ग्लॉसरी मैनेजमेंट
- llm/ LLM प्रोवाइडर इंटरफ़ेस + इम्प्लीमेंटेशन्स
- anthropic.go Anthropic Claude बैकएंड
- openai.go OpenAI / कम्पैटिबल बैकएंड
- gemini.go AI Studio बैकएंड के माध्यम से Google Gemini
- OpenRouter कस्टम base_url के साथ openai.go का उपयोग करता है
- styleguide/ स्टाइल गाइड लोडर
- tm/ JSONL ट्रांसलेशन मेमोरी
- translate/ ट्रांसलेशन ऑर्केस्ट्रेटर
- validate/ लोकेल वैलिडेशन और डिफ़िंग
+ config/ YAML config loading with defaults
+ detect/ Project type auto-detection
+ formats/ Format parsers (JSON, YAML, Markdown)
+ glossary/ Per-locale glossary management
+ llm/ LLM provider interface + implementations
+ anthropic.go Anthropic Claude backend
+ openai.go OpenAI / compatible backend
+ gemini.go Google Gemini via AI Studio backend
+ OpenRouter uses openai.go with custom base_url
+ locale/ BCP 47 identity and CLDR plural categories
+ message/ ICU MessageFormat parser and structural comparison
+ policy/ Stable translation-policy hashing
+ state/ Versioned translation manifest
+ styleguide/ Style guide loader
+ tm/ JSONL translation memory
+ translate/ Translation orchestrator
+ validate/ Locale validation and diffing
```
+
## विकल्पों से तुलना
-| फ़ीचर | Internationalizer | i18next | Crowdin | Generic LLM |
+| सुविधा | Internationalizer | i18next | Crowdin | सामान्य LLM |
|---------|------------------|---------|---------|-------------|
-| LLM-पावर्ड अनुवाद | हाँ | नहीं | आंशिक | हाँ |
+| LLM-संचालित अनुवाद | हाँ | नहीं | आंशिक | हाँ |
| प्रति-भाषा स्टाइल गाइड्स | हाँ | नहीं | नहीं | नहीं |
-| ग्लॉसरी एन्फ़ोर्समेंट | हाँ | नहीं | हाँ | नहीं |
+| ग्लॉसरी प्रवर्तन | हाँ | नहीं | हाँ | नहीं |
| ट्रांसलेशन मेमोरी | हाँ | नहीं | हाँ | नहीं |
-| CLI / लोकल एक्ज़ीक्यूशन | हाँ | लागू नहीं | नहीं | मैन्युअल |
-| Git-फ़्रेंडली फ़ाइलें | हाँ | हाँ | आंशिक | मैन्युअल |
-| कोई SaaS डिपेंडेंसी नहीं | हाँ | हाँ | नहीं | भिन्न होता है |
+| CLI / स्थानीय निष्पादन | हाँ | लागू नहीं | नहीं | मैन्युअल |
+| Git-अनुकूल फ़ाइलें | हाँ | हाँ | आंशिक | मैन्युअल |
+| कोई SaaS निर्भरता नहीं | हाँ | हाँ | नहीं | भिन्न होता है |
| ओपन सोर्स (AGPL-3.0) | हाँ | हाँ | नहीं | भिन्न होता है |
+
## लाइसेंस
-[AGPL-3.0](LICENSE)
+[AGPL-3.0](../../LICENSE)
-## योगदान (Contributing)
+डिपेंडेंसी सूचनाओं के लिए [THIRD_PARTY_NOTICES.md](../../THIRD_PARTY_NOTICES.md) देखें।
-डेवलपमेंट सेटअप और दिशानिर्देशों के लिए [CONTRIBUTING.md](CONTRIBUTING.md) देखें। सभी योगदानों के लिए DCO साइन-ऑफ़ आवश्यक है。
+
+## योगदान
+डेवलपमेंट सेटअप और दिशानिर्देशों के लिए [CONTRIBUTING.md](../../CONTRIBUTING.md) देखें। सभी योगदानों के लिए DCO साइन-ऑफ़ आवश्यक है।
diff --git a/docs/i18n/id.md b/docs/i18n/id.md
index c37f3b9..ddd2a7c 100644
--- a/docs/i18n/id.md
+++ b/docs/i18n/id.md
@@ -6,41 +6,47 @@
# Internationalizer
-Pipeline internasionalisasi berbasis AI untuk proyek perangkat lunak. Terjemahkan, validasi, dan kelola file i18n menggunakan LLM.
+Pipeline internasionalisasi AI-native untuk proyek perangkat lunak. Terjemahkan, validasi, dan kelola berkas i18n menggunakan LLM.
[](https://github.com/Tom-R-Main/Internationalizer/actions/workflows/ci.yml)
-[](LICENSE)
+[](../../LICENSE)
+
+العربية · বাংলা · Čeština · Dansk · Deutsch · Ελληνικά · Español · Suomi · Français · עברית · हिन्दी · Indonesia · Italiano · 日本語 · 한국어 · Bahasa Melayu
Nederlands · ਪੰਜਾਬੀ · Polski · Português · Română · Русский · Svenska · తెలుగు · ไทย · Türkçe · Українська · Tiếng Việt · 粵語 · 简体中文 · 繁體中文
+
+
+---
+
## Mengapa Internationalizer?
-Sebagian besar alat i18n adalah *runtime library* (i18next, react-intl) atau platform SaaS manajemen kunci (Crowdin, Lokalise). Tidak ada satupun yang menyelesaikan masalah terjemahan yang sebenarnya dengan baik:
+Sebagian besar alat i18n berupa pustaka *runtime* (i18next, react-intl) atau platform SaaS manajemen kunci (Crowdin, Lokalise). Belum ada yang menyelesaikan masalah penerjemahan sebenarnya dengan optimal:
-- **Terjemahan manual** tidak dapat diskalakan untuk lebih dari beberapa bahasa
-- **API terjemahan mesin** (Google Translate, DeepL) mengabaikan terminologi, nada, dan konvensi UI Anda
-- **Terjemahan LLM generik** bekerja lebih baik, tetapi tanpa glosarium dan panduan gaya, Anda akan mendapatkan hasil yang tidak konsisten
+- **Penerjemahan manual** tidak dapat diskalakan melampaui beberapa bahasa
+- **API penerjemahan mesin** (Google Translate, DeepL) mengabaikan terminologi, nada, dan konvensi UI Anda
+- **Penerjemahan LLM generik** memang bekerja lebih baik, tetapi tanpa glosarium dan panduan gaya, hasilnya menjadi tidak konsisten
-Internationalizer berbeda. Ini adalah **pipeline CLI** yang menggabungkan terjemahan LLM dengan:
+Internationalizer hadir dengan pendekatan berbeda. Ini adalah **pipeline CLI** yang menggabungkan kemampuan penerjemahan LLM dengan:
-- **Glosarium per bahasa** — menerapkan terminologi yang konsisten di seluruh aplikasi Anda
-- **Panduan gaya per bahasa** — mengontrol nada, formalitas, pluralisasi, dan tipografi
+- **Glosarium per bahasa** — menegakkan konsistensi terminologi di seluruh aplikasi Anda
+- **Panduan gaya per bahasa** — mengontrol nada, tingkat formalitas, bentuk jamak, dan tipografi
- **Memori terjemahan** — melewati string yang tidak berubah, menghemat biaya panggilan API
-- **Validasi kunci** — menangkap terjemahan yang hilang dan ketidakcocokan interpolasi sebelum dirilis
-
+- **Validasi deterministik** — mendeteksi kunci yang hilang atau berlebih, pergeseran struktur terlindungi, pelanggaran glosarium, serta galat bentuk jamak atau ICU sebelum dirilis
+
## Instalasi
-Instal dari npm:
+Instal melalui npm:
```bash
npm install -g internationalizer
```
-Atau jalankan tanpa instalasi global:
+Atau jalankan tanpa penginstalan global:
```bash
npx internationalizer --help
```
-Paket npm menginstal biner bawaan yang cocok dari npm melalui dependensi opsional khusus platform.
+Paket npm menginstal biner siap pakai yang sesuai dari npm melalui dependensi opsional khusus platform.
Instal dengan Go:
@@ -48,30 +54,34 @@ Instal dengan Go:
go install github.com/Tom-R-Main/Internationalizer/cmd/internationalizer@latest
```
-Atau bangun dari sumber:
+Atau bangun dari kode sumber:
```bash
git clone https://github.com/Tom-R-Main/Internationalizer.git
cd Internationalizer
go build -o internationalizer ./cmd/internationalizer
```
-
+
## Paket npm
- Tag Git dan versi paket npm harus cocok, misalnya `v0.1.0` dan `0.1.0`
-- Paket `internationalizer` root bergantung pada paket platform seperti `internationalizer-darwin-arm64`
+- Paket utama `internationalizer` bergantung pada paket platform seperti `internationalizer-darwin-arm64`
- Target npm yang didukung: macOS arm64/x64, Linux arm64/x64, Windows x64
- Publikasi CI memerlukan *secret* GitHub bernama `NPM_TOKEN`
+
+## Memulai Cepat
-## Mulai Cepat
-
-1. Buat file konfigurasi di root proyek Anda:
+1. Buat berkas konfigurasi di direktori root proyek Anda:
```yaml
# .internationalizer.yml
source_locale: en
target_locales: [fr, de, es, ja]
-source_path: locales/en.json
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
llm:
provider: gemini
@@ -91,7 +101,7 @@ export GOOGLE_AI_STUDIO_API_KEY=your-ai-studio-key
internationalizer translate --dry-run
```
-4. Jalankan terjemahan:
+4. Jalankan penerjemahan:
```bash
internationalizer translate
@@ -102,44 +112,72 @@ internationalizer translate
```bash
internationalizer validate
```
-
+
## Perintah
### `translate`
-Temukan kunci yang hilang dan terjemahkan melalui LLM.
+Temukan kunci yang hilang atau kedaluwarsa, lalu terjemahkan melalui LLM.
```bash
-internationalizer translate # terjemahkan semua lokal
-internationalizer translate -l fr # terjemahkan bahasa Prancis saja
-internationalizer translate --dry-run # pratinjau tanpa panggilan API
-internationalizer translate --batch-size 20 # batch lebih kecil
-internationalizer translate --concurrency 2 # panggilan paralel lebih sedikit
+internationalizer translate # translate all locales
+internationalizer translate -l fr # translate French only
+internationalizer translate --dry-run # preview without API calls
+internationalizer translate --adopt-existing # baseline existing translations without API calls
+internationalizer translate --refresh-policy # refresh prompt/style/model-stale entries
+internationalizer translate --batch-size 20 # smaller batches
+internationalizer translate --concurrency 2 # fewer parallel calls
```
+Status penerjemahan melaporkan kondisi kunci secara independen, baik yang hilang (missing), sumber kedaluwarsa (source-stale), kebijakan kedaluwarsa (policy-stale), mutakhir (current), maupun yang diedit manual, sehingga pengeditan manual tidak dapat menyamarkan perubahan sumber atau kebijakan. Nilai dengan kebijakan kedaluwarsa akan dilaporkan tetapi hanya diterjemahkan ulang saat menggunakan opsi `--refresh-policy`. Nilai yang diedit manual tidak akan pernah ditimpa secara otomatis. Gunakan `--adopt-existing` saat memperkenalkan manifes ke terjemahan yang sudah ditinjau atau saat secara eksplisit menerima hasil suntingan manual sebagai *baseline* baru.
+
### `validate`
-Periksa semua file lokal untuk kunci yang hilang, kunci ekstra, dan ketidakcocokan interpolasi.
+Periksa semua berkas lokal terhadap bundel sumbernya. Validasi standar memeriksa cakupan struktural (persentase kunci target yang diperlukan yang tersedia), melaporkan kunci berlebih sebagai peringatan, dan menghasilkan kegagalan jika ada kunci yang hilang, interpolasi tidak cocok, atau struktur ICU MessageFormat tidak valid.
```bash
-internationalizer validate # output yang dapat dibaca manusia
-internationalizer validate --json # JSON yang dapat dibaca mesin
-internationalizer validate -q # hanya kode keluar
+internationalizer validate # human-readable output
+internationalizer validate --json # machine-readable JSON
+internationalizer validate -q # exit code only
+internationalizer validate --strict # enforce translation quality rules
+internationalizer validate --require-state # require current manifest provenance
```
+`--strict` juga melaporkan cakupan yang diterjemahkan. Nilai linguistik yang identik dengan sumbernya dianggap belum diterjemahkan kecuali jika glosarium secara eksplisit mencantumkan entri sumber dan target yang sama persis untuk nilai lengkap tersebut; pengaturan `ignore_case` dipatuhi, tetapi istilah glosarium yang disematkan di dalam nilai yang lebih panjang tidak memenuhi syarat pengecualian. Mode ketat (*strict*) akan gagal jika terdapat kunci ekstra, nilai identik dengan sumber, perubahan struktur interpolasi/HTML/kode/tautan Markdown, pelanggaran glosarium, serta bentuk jamak yang belum dikonfigurasi.
+
+`--require-state` memverifikasi setiap target terhadap `.internationalizer.lock`. Opsi ini akan gagal jika suatu kunci tidak dilacak (*untracked*), atau ketika rekaman sumber, kebijakan penerjemahan, atau hash target telah kedaluwarsa. Opsi ini dapat digabungkan dengan `--strict`.
+
+Laporan teks dan JSON menggunakan kode temuan yang stabil:
+
+| Kode | Arti |
+| --- | --- |
+| `missing_key` / `extra_key` | Himpunan kunci sumber dan target berbeda |
+| `blank_translation` | Sumber non-kosong memiliki target kosong pada mode *strict* |
+| `source_identical` | Nilai linguistik mode *strict* belum diterjemahkan |
+| `protected_structure_mismatch` | Struktur interpolasi, HTML, kode, atau tautan berubah |
+| `glossary_violation` | Istilah target atau varian yang disetujui tidak ditemukan |
+| `plural_form_missing` | Bentuk jamak lokal yang dikonfigurasi tidak ditemukan |
+| `icu_message_syntax` | Pesan ICU sumber atau target salah format |
+| `icu_argument_mismatch` | Nama argumen, tipe, atau gaya pemformat ICU berbeda |
+| `icu_selector_mismatch` | Selektor berbeda atau kategori jamak tidak valid untuk lokal target |
+| `untracked` | Tidak ada rekaman manifes untuk target |
+| `source_stale` | Konten sumber berubah setelah terjemahan direkam |
+| `policy_stale` | Pengaturan prompt atau model yang dihasilkan telah berubah |
+| `target_modified` | Konten target berbeda dari rekaman manifes |
+
### `detect`
-Deteksi otomatis framework i18n dan sarankan konfigurasi.
+Deteksi otomatis framework i18n dan berikan saran konfigurasi.
```bash
internationalizer detect
```
-Mendukung: react-i18next, next-intl, vue-i18n, vanilla JSON, dokumen markdown.
+Mendukung: react-i18next, next-intl, vue-i18n, JSON standar (*vanilla*), dokumen markdown.
### `glossary`
-Kelola istilah glosarium per bahasa yang diterapkan selama penerjemahan.
+Kelola istilah glosarium per bahasa yang wajib diterapkan selama penerjemahan.
```bash
internationalizer glossary list --locale fr
@@ -149,166 +187,225 @@ internationalizer glossary remove --locale fr --source "Dashboard"
### `tm`
-Kelola memori terjemahan (cache JSONL dari string yang diterjemahkan sebelumnya).
+Kelola memori terjemahan (cache JSONL dari string yang telah diterjemahkan sebelumnya).
```bash
-internationalizer tm stats # tampilkan jumlah catatan
-internationalizer tm export # dump sebagai JSON
-internationalizer tm clear --force # hapus semua catatan
+internationalizer tm stats # show record counts
+internationalizer tm export # dump as JSON
+internationalizer tm clear --force # delete all records
```
-
+
## Referensi Konfigurasi
```yaml
# .internationalizer.yml
-# Bahasa sumber (default: en)
+# Source language (default: en)
source_locale: en
-# Bahasa tujuan terjemahan (wajib)
-target_locales: [fr, de, es, ja, zh-CN, ar]
-
-# Jalur ke file lokal sumber (wajib)
-source_path: locales/en.json
-
-# Pengaturan penyedia LLM
+# Languages to translate into (required)
+target_locales: [fr, de, es, ja, yue, zh-CN, zh-TW, ar]
+
+# One or more source-to-target mappings (required).
+# {locale} is replaced with each configured target locale.
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
+ - id: docs
+ source: README.md
+ target: docs/i18n/{locale}.md
+ format: markdown
+
+# Backward compatibility: source_path still maps targets to sibling files
+# such as locales/fr.json. Prefer bundles for new projects.
+# source_path: locales/en.json
+
+# LLM provider settings
llm:
- # Penyedia: "anthropic", "openai", "gemini", atau "openrouter" (default: gemini)
+ # Provider: "anthropic", "openai", "gemini", or "openrouter" (default: gemini)
provider: gemini
- # Nama model default berdasarkan penyedia:
+ # Model name defaults by provider:
# anthropic: claude-opus-5
- # openai: gpt-5.6-luna
+ # openai: gpt-5.6-luna (reasoning effort defaults to max)
# gemini: gemini-3.8-flash
# openrouter: deepseek/deepseek-v4-pro-0813
model: gemini-3.8-flash
- # Variabel lingkungan yang berisi kunci API
+ # Environment variable containing the API key
api_key_env: GOOGLE_AI_STUDIO_API_KEY
- # URL dasar untuk endpoint yang kompatibel dengan OpenAI (opsional)
+ # Base URL for OpenAI-compatible endpoints (optional)
# base_url: https://api.openai.com
-# Kunci per panggilan LLM (default: 40)
+ # OpenAI GPT-5-series Responses API reasoning effort
+ # (default: max for the OpenAI provider)
+ reasoning_effort: max
+
+ # Optional LLM settings for individual target locales. An override using the
+ # global provider inherits unspecified global settings. A different provider
+ # uses that provider's defaults for unspecified settings.
+ locale_overrides:
+ yue:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-CN:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-TW:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+
+# Keys per LLM call (default: 40)
batch_size: 40
-# Panggilan LLM paralel (default: 4)
+# Parallel LLM calls (default: 4)
concurrency: 4
-# Direktori yang berisi file Markdown panduan gaya per lokal (default: style-guides)
+# Directory containing per-locale style guide Markdown files (default: style-guides)
style_guides_dir: style-guides
-# Direktori yang berisi file JSON glosarium per lokal (default: glossary)
+# Directory containing per-locale glossary JSON files (default: glossary)
glossary_dir: glossary
-# Jalur ke file memori terjemahan (default: .internationalizer/tm.jsonl)
+# Path to translation memory file (default: .internationalizer/tm.jsonl)
tm_path: .internationalizer/tm.jsonl
+
+# Versioned source, policy, target, and provenance state
+# (default: .internationalizer.lock; commit this file)
+manifest_path: .internationalizer.lock
+
+# Optional translation and strict-validation rules
+validation:
+ plural_style: i18next-v4 # generate and validate target-locale plural forms
```
+Pengenal lokal harus berupa tag BCP 47 yang terformat dengan benar seperti `fr`, `pt-BR`, atau `sr-Latn-RS`. Lokal target yang setara secara kanonikal akan ditolak sebagai duplikat, dan penimpaan penyedia khusus lokal dicocokkan dengan ejaan yang setara secara kanonikal. Pada contoh di atas, lokal tanpa penimpaan—termasuk bahasa Jepang—akan mewarisi konfigurasi global Gemini.
+
+Nilai ICU MessageFormat diurai secara struktural. Argumen sederhana, `select`, `plural`, `selectordinal`, `number`, `date`, dan `time` didukung, termasuk pesan bersarang, *offset* bentuk jamak, selektor angka persis, serta `#`. Validasi memeriksa sintaksis, tipe argumen, gaya pemformat, *offset* bentuk jamak, identitas cabang *select*, serta kategori jamak CLDR lokal target. Keluaran penyedia yang merusak invarian ini akan ditolak sebelum berkas lokal atau rekaman memori terjemahan ditulis.
+
+Dengan `i18next-v4`, famili jamak sumber yang dikenali diperluas selama penerjemahan ke kategori CLDR lokal target. Kategori khusus target menggunakan nilai `_other` famili sumber sebagai templat penerjemahannya. Validasi *strict* mewajibkan kategori target tersebut; kategori khusus sumber bersifat opsional bagi lokal target yang tidak menggunakannya.
+
## Panduan Gaya
-Panduan gaya adalah file Markdown yang disuntikkan ke dalam prompt terjemahan LLM. Panduan ini mengontrol nada, formalitas, tipografi, dan konvensi khusus bahasa lainnya.
+Panduan gaya adalah berkas Markdown yang disuntikkan ke dalam prompt penerjemahan LLM. Panduan ini mengontrol nada, tingkat formalitas, tipografi, dan konvensi khusus bahasa lainnya.
```
style-guides/
- _conventions.md # aturan bersama untuk semua bahasa
- fr.md # aturan khusus bahasa Prancis
- ja.md # aturan khusus bahasa Jepang
- ar.md # aturan khusus bahasa Arab
+ _conventions.md # shared rules for all languages
+ fr.md # French-specific rules
+ ja.md # Japanese-specific rules
+ ar.md # Arabic-specific rules
```
### Konvensi bersama (`_conventions.md`)
-Tentukan aturan yang berlaku untuk semua bahasa: sintaks interpolasi, pelestarian HTML, konvensi tipe string (tombol vs. label vs. error), dll.
+Mendefinisikan aturan yang berlaku untuk semua bahasa: sintaksis interpolasi, pelestarian HTML, konvensi tipe string (tombol vs. label vs. galat), dll.
### Panduan per bahasa (`{locale}.md`)
-Tentukan aturan khusus bahasa: tingkat formalitas (tu vs. vous), tanda baca (guillemet, tanda tanya terbalik), bentuk jamak, pemformatan tanggal/angka, dan glosarium terminologi.
+Mendefinisikan aturan khusus bahasa: tingkat formalitas (tu vs. vous), tanda baca (guillemet, tanda tanya terbalik), bentuk jamak, pemformatan tanggal/angka, dan glosarium terminologi.
-Lihat [`examples/react-app/style-guides/`](examples/react-app/style-guides/) untuk contoh yang berfungsi.
+Panduan gaya merupakan input kebijakan yang tahan lama (*durable*), bukan hasil yang di-*generate*. Internationalizer membacanya namun tidak pernah menulis ulang berkas tersebut. Isinya di-hash secara terpisah dari glosarium dan kontrak prompt, sehingga perubahan kode aplikasi tidak menyebabkan terjemahan menjadi kedaluwarsa. Mengedit panduan gaya secara sengaja menandai lokal tersebut untuk ditinjau ulang kebijakannya; mengubah susunan kata prompt internal tidak memicunya, kecuali jika versi kontrak prompt juga berubah.
+Lihat [`examples/react-app/style-guides/`](../../examples/react-app/style-guides/) untuk contoh yang berfungsi.
+
## Format Glosarium
-File glosarium adalah array JSON yang disimpan di `{glossary_dir}/{locale}.json`:
+Berkas glosarium adalah array JSON yang disimpan di `{glossary_dir}/{locale}.json`:
```json
[
{
"source": "Dashboard",
"target": "Tableau de bord",
+ "variants": ["Panneau de contrôle"],
+ "enforcement": "error",
"ignore_case": false,
"whole_word": true
}
]
```
-Istilah disuntikkan ke dalam prompt LLM sebagai tabel terminologi, memastikan terjemahan istilah kunci yang konsisten di seluruh aplikasi Anda.
-
+`variants` mencantumkan bentuk target lain yang disetujui. `enforcement` dapat bernilai `error`, `warning`, atau diabaikan untuk perilaku bawaan galat. Istilah disuntikkan ke dalam prompt LLM sebagai tabel terminologi, memastikan penerjemahan yang konsisten di seluruh aplikasi Anda. Entri persis seperti `{"source":"API","target":"API"}` juga mengecualikan nilai lengkap yang identik dengan sumber tersebut dari temuan nilai belum diterjemahkan pada mode *strict*; entri ini tidak mengecualikan nilai yang lebih panjang yang hanya sekadar mengandung `API`.
+
## Memori Terjemahan
-Memori terjemahan disimpan sebagai file JSONL (satu catatan JSON per baris). Setiap catatan berisi:
-
-- Kunci dan nilai sumber
-- Nilai yang diterjemahkan
-- Hash SHA-256 dari nilai sumber
-- Stempel waktu
+Memori terjemahan disimpan sebagai berkas JSONL (satu rekaman JSON per baris). Setiap rekaman berisi:
-Pada proses berikutnya, string yang tidak berubah disajikan dari cache TM tanpa memanggil LLM, menghemat waktu dan biaya API. File TM ramah-git dan dapat di-commit bersama file lokal Anda.
+- Bundel, kunci, nilai sumber, nilai terjemahan, dan lokal target kanonikal
+- Hash sumber, panduan gaya, glosarium, kontrak prompt, dan kebijakan gabungan
+- Penyedia dan model yang menghasilkan terjemahan
+- Stempel waktu (*timestamp*)
+Pada eksekusi berikutnya, string dengan hash sumber dan kebijakan yang sama diambil langsung dari cache tanpa memanggil LLM. Jalur default berada di bawah direktori yang diabaikan `.internationalizer/`, sehingga tetap menjadi cache lokal. Atur `tm_path` ke lokasi yang dilacak Git jika proyek Anda memang ingin membagikan memori terjemahan secara sengaja. Manifes `.internationalizer.lock` yang dapat ditinjau diatur versinya secara terpisah.
+
## Format yang Didukung
| Format | Ekstensi | Mode |
|--------|-----------|------|
-| JSON | `.json` | Key-value (bersarang, diratakan dengan notasi titik) |
-| YAML | `.yml`, `.yaml` | Key-value (mempertahankan komentar dan urutan) |
-| Markdown | `.md`, `.mdx` | Terjemahan seluruh dokumen |
+| JSON | `.json` | Kunci-nilai (bersarang, diratakan dengan notasi titik) |
+| YAML | `.yml`, `.yaml` | Kunci-nilai (mempertahankan komentar dan urutan) |
+| Markdown | `.md`, `.mdx` | Pembuka (*preamble*) dan bagian tingkat H2 |
+Target Markdown memuat komentar tersembunyi `internationalizer:unit` sebelum bagian H2. Penanda stabil ini memungkinkan Internationalizer menambahkan, memindahkan, atau mengedit satu bagian sumber tanpa menerjemahkan ulang bagian lain yang tidak terkait. Dokumen yang belum memiliki penanda akan otomatis menerimanya pada pembaruan berhasil berikutnya.
+
## Deteksi Jenis Proyek
-`internationalizer detect` mengidentifikasi pengaturan i18n Anda dengan memeriksa:
+`internationalizer detect` mengidentifikasi konfigurasi i18n Anda dengan memeriksa:
- Dependensi `package.json` untuk react-i18next, next-intl, atau vue-i18n
- Struktur direktori yang cocok dengan pola lokal umum
-- Ekstensi file dan konvensi penamaan
-
+- Ekstensi berkas dan konvensi penamaan
+
## Arsitektur
```
-cmd/internationalizer/ Titik masuk CLI dan definisi perintah
+cmd/internationalizer/ CLI entry point and command definitions
internal/
- config/ Pemuatan konfigurasi YAML dengan default
- detect/ Deteksi otomatis jenis proyek
- formats/ Parser format (JSON, YAML, Markdown)
- glossary/ Manajemen glosarium per lokal
- llm/ Antarmuka penyedia LLM + implementasi
- anthropic.go Backend Anthropic Claude
- openai.go Backend OpenAI / kompatibel
- gemini.go Backend Google Gemini via AI Studio
- OpenRouter menggunakan openai.go dengan base_url kustom
- styleguide/ Pemuat panduan gaya
- tm/ Memori terjemahan JSONL
- translate/ Orkestrator terjemahan
- validate/ Validasi lokal dan diffing
+ config/ YAML config loading with defaults
+ detect/ Project type auto-detection
+ formats/ Format parsers (JSON, YAML, Markdown)
+ glossary/ Per-locale glossary management
+ llm/ LLM provider interface + implementations
+ anthropic.go Anthropic Claude backend
+ openai.go OpenAI / compatible backend
+ gemini.go Google Gemini via AI Studio backend
+ OpenRouter uses openai.go with custom base_url
+ locale/ BCP 47 identity and CLDR plural categories
+ message/ ICU MessageFormat parser and structural comparison
+ policy/ Stable translation-policy hashing
+ state/ Versioned translation manifest
+ styleguide/ Style guide loader
+ tm/ JSONL translation memory
+ translate/ Translation orchestrator
+ validate/ Locale validation and diffing
```
-
+
## Perbandingan dengan Alternatif
| Fitur | Internationalizer | i18next | Crowdin | LLM Generik |
|---------|------------------|---------|---------|-------------|
-| Terjemahan bertenaga LLM | Ya | Tidak | Sebagian | Ya |
+| Penerjemahan berbasis LLM | Ya | Tidak | Sebagian | Ya |
| Panduan gaya per bahasa | Ya | Tidak | Tidak | Tidak |
-| Penerapan glosarium | Ya | Tidak | Ya | Tidak |
+| Penegakan glosarium | Ya | Tidak | Ya | Tidak |
| Memori terjemahan | Ya | Tidak | Ya | Tidak |
| Eksekusi CLI / lokal | Ya | N/A | Tidak | Manual |
-| File ramah-Git | Ya | Ya | Sebagian | Manual |
+| Berkas ramah Git | Ya | Ya | Sebagian | Manual |
| Tanpa dependensi SaaS | Ya | Ya | Tidak | Bervariasi |
-| Open source (AGPL-3.0) | Ya | Ya | Tidak | Bervariasi |
-
+| Sumber terbuka (AGPL-3.0) | Ya | Ya | Tidak | Bervariasi |
+
## Lisensi
-[AGPL-3.0](LICENSE)
+[AGPL-3.0](../../LICENSE)
+Lihat [THIRD_PARTY_NOTICES.md](../../THIRD_PARTY_NOTICES.md) untuk pemberitahuan dependensi.
+
## Berkontribusi
-Lihat [CONTRIBUTING.md](CONTRIBUTING.md) untuk pengaturan pengembangan dan panduan. Semua kontribusi memerlukan persetujuan DCO.
-
+Lihat [CONTRIBUTING.md](../../CONTRIBUTING.md) untuk panduan dan penyiapan pengembangan. Semua kontribusi memerlukan persetujuan DCO.
diff --git a/docs/i18n/it.md b/docs/i18n/it.md
index 990f8fe..41f0646 100644
--- a/docs/i18n/it.md
+++ b/docs/i18n/it.md
@@ -9,30 +9,31 @@
Pipeline di internazionalizzazione nativa per l'AI per progetti software. Traduci, convalida e gestisci i file i18n usando gli LLM.
[](https://github.com/Tom-R-Main/Internationalizer/actions/workflows/ci.yml)
-[](LICENSE)
+[](../../LICENSE)
-العربية · বাংলা · Deutsch · Español · Français · हिन्दी · Indonesia · 日本語 · ਪੰਜਾਬੀ · Português · Русский · తెలుగు
-ไทย · Українська · 粵語 · 简体中文 · 繁體中文
+العربية · বাংলা · Čeština · Dansk · Deutsch · Ελληνικά · Español · Suomi · Français · עברית · हिन्दी · Indonesia · Italiano · 日本語 · 한국어 · Bahasa Melayu
Nederlands · ਪੰਜਾਬੀ · Polski · Português · Română · Русский · Svenska · తెలుగు · ไทย · Türkçe · Українська · Tiếng Việt · 粵語 · 简体中文 · 繁體中文
---
+
## Perché Internationalizer?
-La maggior parte degli strumenti i18n sono librerie di runtime (i18next, react-intl) o piattaforme SaaS per la gestione delle chiavi (Crowdin, Lokalise). Nessuno di questi risolve bene il vero problema della traduzione:
+La maggior parte degli strumenti i18n sono librerie di runtime (i18next, react-intl) o piattaforme SaaS per la gestione delle chiavi (Crowdin, Lokalise). Nessuno di essi risolve davvero a fondo il problema della traduzione:
-- **La traduzione manuale** non scala oltre poche lingue
-- **Le API di traduzione automatica** (Google Translate, DeepL) ignorano la tua terminologia, il tono e le convenzioni della UI
-- **La traduzione LLM generica** funziona meglio, ma senza glossari e guide di stile ottieni risultati incoerenti
+- **La traduzione manuale** non scala oltre un numero limitato di lingue
+- **Le API di traduzione automatica** (Google Translate, DeepL) ignorano la tua terminologia, il tono e le convenzioni dell'interfaccia utente
+- **La traduzione con LLM generici** funziona meglio, ma senza glossari e guide di stile produce risultati incoerenti
-Internationalizer è diverso. È una **pipeline CLI** che combina la traduzione LLM con:
+Internationalizer è diverso. È una **pipeline CLI** che combina la traduzione con LLM con:
-- **Glossari per lingua** — applicano una terminologia coerente in tutta l'app
-- **Guide di stile per lingua** — controllano tono, formalità, pluralizzazione e tipografia
-- **Memoria di traduzione** — salta le stringhe non modificate, risparmiando sulle chiamate API
-- **Convalida delle chiavi** — individua traduzioni mancanti e mancate corrispondenze di interpolazione prima del rilascio
+- **Glossari per lingua** — garantiscono una terminologia coerente in tutta l'applicazione
+- **Guide di stile per lingua** — definiscono tono, registro di formalità, pluralizzazione e tipografia
+- **Memoria di traduzione** — salta le stringhe invariate, risparmiando sui costi delle chiamate API
+- **Convalida deterministica** — individua chiavi mancanti o in eccesso, derive della struttura protetta, problemi del glossario ed errori nei plurali o nei messaggi ICU prima del rilascio
+
## Installazione
Installa da npm:
@@ -47,7 +48,7 @@ Oppure esegui senza un'installazione globale:
npx internationalizer --help
```
-Il pacchetto npm installa il binario precompilato corrispondente da npm tramite dipendenze opzionali specifiche per la piattaforma.
+Il pacchetto npm installa il file binario precompilato corrispondente da npm tramite dipendenze opzionali specifiche per la piattaforma.
Installa con Go:
@@ -63,22 +64,28 @@ cd Internationalizer
go build -o internationalizer ./cmd/internationalizer
```
+
## Pacchetti npm
- I tag Git e le versioni dei pacchetti npm devono corrispondere, ad esempio `v0.1.0` e `0.1.0`
-- Il pacchetto radice `internationalizer` dipende da pacchetti di piattaforma come `internationalizer-darwin-arm64`
+- Il pacchetto radice `internationalizer` dipende da pacchetti specifici per piattaforma come `internationalizer-darwin-arm64`
- Target npm supportati: macOS arm64/x64, Linux arm64/x64, Windows x64
-- La pubblicazione CI richiede un secret di GitHub chiamato `NPM_TOKEN`
+- La pubblicazione tramite CI richiede un secret di GitHub denominato `NPM_TOKEN`
+
## Avvio rapido
-1. Crea un file di configurazione nella radice del tuo progetto:
+1. Crea un file di configurazione nella radice del progetto:
```yaml
# .internationalizer.yml
source_locale: en
target_locales: [fr, de, es, ja]
-source_path: locales/en.json
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
llm:
provider: gemini
@@ -86,13 +93,13 @@ llm:
api_key_env: GOOGLE_AI_STUDIO_API_KEY
```
-2. Imposta la tua chiave API:
+2. Imposta la chiave API:
```bash
export GOOGLE_AI_STUDIO_API_KEY=your-ai-studio-key
```
-3. Visualizza in anteprima cosa verrà tradotto:
+3. Visualizza in anteprima gli elementi che verranno tradotti:
```bash
internationalizer translate --dry-run
@@ -110,43 +117,72 @@ internationalizer translate
internationalizer validate
```
+
## Comandi
### `translate`
-Trova le chiavi mancanti e traducile tramite un LLM.
+Individua le chiavi mancanti o obsolete e traducile tramite un LLM.
```bash
internationalizer translate # translate all locales
internationalizer translate -l fr # translate French only
internationalizer translate --dry-run # preview without API calls
+internationalizer translate --adopt-existing # baseline existing translations without API calls
+internationalizer translate --refresh-policy # refresh prompt/style/model-stale entries
internationalizer translate --batch-size 20 # smaller batches
internationalizer translate --concurrency 2 # fewer parallel calls
```
+Lo stato della traduzione segnala in modo indipendente le condizioni di elementi mancanti, con sorgente obsoleta, con criteri obsoleti, aggiornati e modificati manualmente; in questo modo una modifica manuale non può nascondere una modifica ai sorgenti o ai criteri. I valori con criteri obsoleti vengono segnalati ma ritradotti solo specificando `--refresh-policy`. I valori modificati manualmente non vengono mai sovrascritti automaticamente. Usa `--adopt-existing` quando introduci il manifest su traduzioni già revisionate o quando intendi accettare esplicitamente una modifica manuale revisionata come nuova baseline.
+
### `validate`
-Controlla tutti i file delle lingue per chiavi mancanti, chiavi extra e mancate corrispondenze di interpolazione.
+Verifica tutti i file di lingua rispetto ai relativi bundle di origine. La convalida predefinita verifica la copertura strutturale (la percentuale di chiavi di destinazione richieste presenti), segnala le chiavi in eccesso come avvisi e restituisce un errore in presenza di chiavi mancanti, discrepanze di interpolazione o strutture ICU MessageFormat non valide.
```bash
internationalizer validate # human-readable output
internationalizer validate --json # machine-readable JSON
internationalizer validate -q # exit code only
+internationalizer validate --strict # enforce translation quality rules
+internationalizer validate --require-state # require current manifest provenance
```
+`--strict` segnala inoltre la percentuale di copertura tradotta. Un valore linguistico identico alla sorgente è considerato non tradotto, a meno che il glossario non contenga una voce identica per sorgente e destinazione per l'intero valore; l'opzione `ignore_case` viene rispettata, ma un termine di glossario presente all'interno di un valore più lungo non costituisce un'esenzione. La modalità strict genera un errore in caso di chiavi in eccesso, valori identici alla sorgente, alterazioni nella struttura di interpolazione/HTML/codice/link Markdown, violazioni del glossario e forme plurali configurate non conformi.
+
+`--require-state` verifica ogni destinazione rispetto al file `.internationalizer.lock`. Restituisce un errore se una chiave non è tracciata o se il valore registrato per sorgente, criteri di traduzione o hash di destinazione risulta obsoleto. Può essere combinato con `--strict`.
+
+I report per l'utente e in formato JSON usano codici di riscontro stabili:
+
+| Codice | Significato |
+| --- | --- |
+| `missing_key` / `extra_key` | I set di chiavi di origine e destinazione differiscono |
+| `blank_translation` | Una sorgente non vuota presenta una destinazione vuota in modalità strict |
+| `source_identical` | Un valore linguistico in modalità strict non è stato tradotto |
+| `protected_structure_mismatch` | La struttura di interpolazione, HTML, codice o link è cambiata |
+| `glossary_violation` | Non è stato trovato alcun termine di destinazione o variante approvata |
+| `plural_form_missing` | Una forma plurale configurata per la lingua è assente |
+| `icu_message_syntax` | Un messaggio ICU di origine o destinazione presenta errori di sintassi |
+| `icu_argument_mismatch` | I nomi, i tipi o gli stili di formattazione degli argomenti ICU differiscono |
+| `icu_selector_mismatch` | I selettori differiscono oppure una categoria plurale non è valida per la lingua di destinazione |
+| `untracked` | Non è presente alcun record nel manifest per la destinazione |
+| `source_stale` | Il contenuto di origine è stato modificato dopo la traduzione registrata |
+| `policy_stale` | Il prompt generato o le impostazioni del modello sono cambiati |
+| `target_modified` | Il contenuto di destinazione differisce dal record presente nel manifest |
+
### `detect`
-Rileva automaticamente il framework i18n e suggerisce una configurazione.
+Rileva automaticamente il framework i18n in uso e propone una configurazione.
```bash
internationalizer detect
```
-Supporta: react-i18next, next-intl, vue-i18n, JSON vanilla, documenti markdown.
+Supporta: react-i18next, next-intl, vue-i18n, JSON standard, documentazione Markdown.
### `glossary`
-Gestisci i termini del glossario per lingua che vengono applicati durante la traduzione.
+Gestisci i termini del glossario per lingua applicati durante la traduzione.
```bash
internationalizer glossary list --locale fr
@@ -164,6 +200,7 @@ internationalizer tm export # dump as JSON
internationalizer tm clear --force # delete all records
```
+
## Riferimento per la configurazione
```yaml
@@ -173,10 +210,23 @@ internationalizer tm clear --force # delete all records
source_locale: en
# Languages to translate into (required)
-target_locales: [fr, de, es, ja, zh-CN, ar]
-
-# Path to the source locale file (required)
-source_path: locales/en.json
+target_locales: [fr, de, es, ja, yue, zh-CN, zh-TW, ar]
+
+# One or more source-to-target mappings (required).
+# {locale} is replaced with each configured target locale.
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
+ - id: docs
+ source: README.md
+ target: docs/i18n/{locale}.md
+ format: markdown
+
+# Backward compatibility: source_path still maps targets to sibling files
+# such as locales/fr.json. Prefer bundles for new projects.
+# source_path: locales/en.json
# LLM provider settings
llm:
@@ -185,7 +235,7 @@ llm:
# Model name defaults by provider:
# anthropic: claude-opus-5
- # openai: gpt-5.6-luna
+ # openai: gpt-5.6-luna (reasoning effort defaults to max)
# gemini: gemini-3.8-flash
# openrouter: deepseek/deepseek-v4-pro-0813
model: gemini-3.8-flash
@@ -196,6 +246,27 @@ llm:
# Base URL for OpenAI-compatible endpoints (optional)
# base_url: https://api.openai.com
+ # OpenAI GPT-5-series Responses API reasoning effort
+ # (default: max for the OpenAI provider)
+ reasoning_effort: max
+
+ # Optional LLM settings for individual target locales. An override using the
+ # global provider inherits unspecified global settings. A different provider
+ # uses that provider's defaults for unspecified settings.
+ locale_overrides:
+ yue:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-CN:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-TW:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+
# Keys per LLM call (default: 40)
batch_size: 40
@@ -210,11 +281,26 @@ glossary_dir: glossary
# Path to translation memory file (default: .internationalizer/tm.jsonl)
tm_path: .internationalizer/tm.jsonl
+
+# Versioned source, policy, target, and provenance state
+# (default: .internationalizer.lock; commit this file)
+manifest_path: .internationalizer.lock
+
+# Optional translation and strict-validation rules
+validation:
+ plural_style: i18next-v4 # generate and validate target-locale plural forms
```
+Gli identificatori delle impostazioni internazionali devono essere tag BCP 47 corretti come `fr`, `pt-BR` o `sr-Latn-RS`. Le impostazioni internazionali di destinazione con equivalenza canonica vengono rifiutate come duplicati e gli override del provider specifici per lingua corrispondono alla grafia canonicamente equivalente. Nell'esempio precedente, le impostazioni internazionali senza override (incluso il giapponese) ereditano la configurazione globale di Gemini.
+
+I valori in formato ICU MessageFormat vengono analizzati in modo strutturale. Sono supportati argomenti semplici, `select`, `plural`, `selectordinal`, `number`, `date` e `time`, inclusi messaggi annidati, offset dei plurali, selettori di numeri esatti e `#`. La convalida verifica la sintassi, i tipi di argomenti e gli stili di formattazione, gli offset dei plurali, l'identità dei rami del costrutto select e le categorie dei plurali CLDR della lingua di destinazione. L'output del provider che viola queste invarianti viene rifiutato prima che venga scritto un file di lingua o un record della memoria di traduzione.
+
+Con `i18next-v4`, le famiglie di plurali di origine riconosciute vengono espanse durante la traduzione nelle categorie CLDR della lingua di destinazione. Una categoria presente solo nella destinazione usa il valore `_other` della famiglia di origine come modello di traduzione. La convalida strict richiede la presenza di tali categorie di destinazione; le categorie presenti solo nella sorgente sono facoltative per le lingue di destinazione che non le prevedono.
+
+
## Guide di stile
-Le guide di stile sono file Markdown che vengono iniettati nel prompt di traduzione dell'LLM. Controllano il tono, la formalità, la tipografia e altre convenzioni specifiche della lingua.
+Le guide di stile sono file Markdown inseriti nel prompt di traduzione dell'LLM. Definiscono tono, registro di formalità, tipografia e altre convenzioni specifiche per ogni lingua.
```
style-guides/
@@ -226,58 +312,69 @@ style-guides/
### Convenzioni condivise (`_conventions.md`)
-Definisci le regole che si applicano a tutte le lingue: sintassi di interpolazione, conservazione dell'HTML, convenzioni sui tipi di stringa (pulsanti vs. etichette vs. errori), ecc.
+Definiscono le regole valide per tutte le lingue: sintassi di interpolazione, conservazione dell'HTML, convenzioni sui tipi di stringa (pulsanti, etichette, errori) e altro ancora.
### Guide per lingua (`{locale}.md`)
-Definisci le regole specifiche della lingua: registro di formalità (tu vs. vous), punteggiatura (virgolette, punti interrogativi invertiti), forme plurali, formattazione di date/numeri e un glossario terminologico.
+Definiscono regole specifiche per la lingua: registro di formalità (tu vs. Lei/voi), punteggiatura (virgolette caporali, punti interrogativi invertiti), forme plurali, formattazione di date/numeri e un glossario terminologico.
-Vedi [`examples/react-app/style-guides/`](examples/react-app/style-guides/) per un esempio funzionante.
+Le guide di stile rappresentano criteri durevoli, non file generati. Internationalizer le legge ma non le riscrive mai. Il loro contenuto viene sottoposto a hash separatamente dal glossario e dal contratto del prompt, in modo che una modifica al codice dell'applicazione non renda obsoleta una traduzione. La modifica di una guida contrassegna intenzionalmente tale lingua per la revisione dei criteri; la sola modifica del testo interno dei prompt non produce questo effetto, a meno che non cambi anche la versione del contratto del prompt.
+Vedi [`examples/react-app/style-guides/`](../../examples/react-app/style-guides/) per un esempio pratico.
+
+
## Formato del glossario
-I file del glossario sono array JSON memorizzati in `{glossary_dir}/{locale}.json`:
+I file di glossario sono array JSON archiviati nel percorso `{glossary_dir}/{locale}.json`:
```json
[
{
"source": "Dashboard",
"target": "Tableau de bord",
+ "variants": ["Panneau de contrôle"],
+ "enforcement": "error",
"ignore_case": false,
"whole_word": true
}
]
```
-I termini vengono iniettati nel prompt dell'LLM come tabella terminologica, garantendo una traduzione coerente dei termini chiave in tutta l'applicazione.
+`variants` elenca le altre forme approvate per la destinazione. `enforcement` può essere impostato su `error`, `warning` oppure omesso per applicare il comportamento predefinito di errore. I termini vengono inseriti nel prompt dell'LLM sotto forma di tabella terminologica, garantendo coerenza nelle traduzioni in tutta l'applicazione. Una voce esatta come `{"source":"API","target":"API"}` esenta inoltre il valore completo identico all'originale dalle segnalazioni di mancata traduzione in modalità strict; la presenza del termine `API` all'interno di un valore più lungo non costituisce invece un'esenzione.
+
## Memoria di traduzione
-La memoria di traduzione è memorizzata come file JSONL (un record JSON per riga). Ogni record contiene:
+La memoria di traduzione è memorizzata in un file JSONL (un record JSON per riga). Ciascun record include:
-- La chiave e il valore di origine
-- Il valore tradotto
-- Un hash SHA-256 del valore di origine
+- Bundle, chiave, valore di origine, valore tradotto e impostazione internazionale canonica di destinazione
+- Hash di origine, guida di stile, glossario, contratto del prompt e hash combinato dei criteri
+- Provider e modello che hanno generato la traduzione
- Un timestamp
-Nelle esecuzioni successive, le stringhe non modificate vengono fornite dalla cache della TM senza chiamare l'LLM, risparmiando sia tempo che costi delle API. Il file della TM è compatibile con git e può essere committato insieme ai file delle lingue.
+Nelle esecuzioni successive, le stringhe aventi gli stessi hash di sorgente e criteri vengono recuperate direttamente dalla cache senza chiamare l'LLM. Il percorso predefinito si trova all'interno della directory ignorata `.internationalizer/`, mantenendo la cache in locale. Imposta `tm_path` su una directory tracciata se il progetto prevede la condivisione esplicita della memoria di traduzione. Il file manifest verificabile `.internationalizer.lock` viene invece gestito con un proprio controllo di versione separato.
+
## Formati supportati
| Formato | Estensioni | Modalità |
-|--------|-----------|------|
-| JSON | `.json` | Chiave-valore (nidificato, appiattito con notazione a punti) |
+|---|---|---|
+| JSON | `.json` | Chiave-valore (annidato, appiattito con notazione puntata) |
| YAML | `.yml`, `.yaml` | Chiave-valore (preserva commenti e ordinamento) |
-| Markdown | `.md`, `.mdx` | Traduzione dell'intero documento |
+| Markdown | `.md`, `.mdx` | Preambolo e sezioni di livello H2 |
+
+I file Markdown di destinazione includono commenti invisibili `internationalizer:unit` prima delle sezioni H2. Questi marcatori stabili consentono a Internationalizer di aggiungere, spostare o modificare una sezione di origine senza dover ritradurre le sezioni non correlate. I documenti esistenti privi di marcatori li riceveranno in occasione del successivo aggiornamento eseguito con successo.
+
## Rilevamento del tipo di progetto
-`internationalizer detect` identifica la tua configurazione i18n controllando:
+`internationalizer detect` analizza l'impostazione i18n del progetto controllando:
-- Le dipendenze in `package.json` per react-i18next, next-intl o vue-i18n
-- Le strutture delle directory che corrispondono a pattern comuni per le lingue
+- Le dipendenze in `package.json` relative a react-i18next, next-intl o vue-i18n
+- Le strutture delle directory corrispondenti a schemi di localizzazione diffusi
- Le estensioni dei file e le convenzioni di denominazione
+
## Architettura
```
@@ -292,30 +389,38 @@ internal/
openai.go OpenAI / compatible backend
gemini.go Google Gemini via AI Studio backend
OpenRouter uses openai.go with custom base_url
+ locale/ BCP 47 identity and CLDR plural categories
+ message/ ICU MessageFormat parser and structural comparison
+ policy/ Stable translation-policy hashing
+ state/ Versioned translation manifest
styleguide/ Style guide loader
tm/ JSONL translation memory
translate/ Translation orchestrator
validate/ Locale validation and diffing
```
+
## Confronto con le alternative
| Funzionalità | Internationalizer | i18next | Crowdin | LLM generico |
-|---------|------------------|---------|---------|-------------|
+|---|---|---|---|---|
| Traduzione basata su LLM | Sì | No | Parziale | Sì |
| Guide di stile per lingua | Sì | No | No | No |
| Applicazione del glossario | Sì | No | Sì | No |
| Memoria di traduzione | Sì | No | Sì | No |
-| CLI / esecuzione locale | Sì | N/D | No | Manuale |
+| Esecuzione da CLI / in locale | Sì | N/D | No | Manuale |
| File compatibili con Git | Sì | Sì | Parziale | Manuale |
-| Nessuna dipendenza SaaS | Sì | Sì | No | Varia |
-| Open source (AGPL-3.0) | Sì | Sì | No | Varia |
+| Nessuna dipendenza SaaS | Sì | Sì | No | Variabile |
+| Open source (AGPL-3.0) | Sì | Sì | No | Variabile |
+
## Licenza
-[AGPL-3.0](LICENSE)
+[AGPL-3.0](../../LICENSE)
-## Contribuire
+Consulta [THIRD_PARTY_NOTICES.md](../../THIRD_PARTY_NOTICES.md) per le comunicazioni sulle dipendenze.
-Vedi [CONTRIBUTING.md](CONTRIBUTING.md) per la configurazione dello sviluppo e le linee guida. Tutti i contributi richiedono l'approvazione DCO.
+
+## Contribuire
+Consulta [CONTRIBUTING.md](../../CONTRIBUTING.md) per le linee guida e la configurazione dell'ambiente di sviluppo. Tutti i contributi richiedono la firma del DCO (Developer Certificate of Origin).
diff --git a/docs/i18n/ja.md b/docs/i18n/ja.md
index fb73950..4733c3e 100644
--- a/docs/i18n/ja.md
+++ b/docs/i18n/ja.md
@@ -16,62 +16,62 @@
---
-
+
## Internationalizerを選ぶ理由
-一般的なi18nツールの多くは、ランタイムライブラリ(i18next、react-intl)か、キー管理SaaSプラットフォーム(Crowdin、Lokalise)のいずれかです。しかし、実際の翻訳課題を適切に解決できているツールはありません。
+多くのi18nツールは、ランタイムライブラリ(i18next、react-intl)か、キー管理SaaSプラットフォーム(Crowdin、Lokalise)のいずれかです。しかし、実際の翻訳課題を適切に解決できているツールはありません。
-- **手動翻訳**は、数言語を超えると対応しきれなくなります
-- **機械翻訳API**(Google翻訳、DeepL)は、専門用語、トーン、UIの規則を無視します
-- **汎用LLM翻訳**は精度が高いものの、用語集やスタイルガイドがなければ出力結果にばらつきが生じます
+- **手動翻訳**は、数言語を超えるとスケールしません
+- **機械翻訳API**(Google翻訳、DeepL)は、独自の用語集、トーン、UIの慣例を無視します
+- **汎用LLM翻訳**はより高精度ですが、用語集やスタイルガイドがなければ出力結果にばらつきが生じます
Internationalizerは異なります。LLM翻訳と以下の機能を組み合わせた**CLIパイプライン**です。
- **言語ごとの用語集** — アプリケーション全体で一貫した用語を適用
- **言語ごとのスタイルガイド** — トーン、丁寧さの度合い、複数形、タイポグラフィを制御
- **翻訳メモリ** — 変更のない文字列をスキップし、API呼び出しコストを削減
-- **決定論的な検証** — リリース前にキーの欠落や余分なキー、保護対象の構造の差異、用語集違反、複数形やICUのエラーを検出
-
+- **決定論的な検証** — キーの欠落や余分なキー、保護対象構造の乖離、用語集違反、複数形やICUのエラーをリリース前に検出
+
## インストール
-npmでのインストール
+npmからインストール:
```bash
npm install -g internationalizer
```
-グローバルインストールせずに実行
+またはグローバルインストールなしで実行:
```bash
npx internationalizer --help
```
-npmパッケージは、プラットフォーム固有のオプション依存関係を介して、対応するビルド済みバイナリをnpmから自動的にインストールします。
+npmパッケージは、プラットフォーム固有のオプションの依存関係を介して、一致するビルド済みバイナリをnpmからインストールします。
-Goでのインストール
+Goでインストール:
```bash
go install github.com/Tom-R-Main/Internationalizer/cmd/internationalizer@latest
```
-ソースからのビルド
+またはソースからビルド:
```bash
git clone https://github.com/Tom-R-Main/Internationalizer.git
cd Internationalizer
go build -o internationalizer ./cmd/internationalizer
```
-
+
## npmパッケージ
-- Gitタグとnpmパッケージのバージョンは一致している必要があります(例:`v0.1.0`と`0.1.0`)
+- Gitタグとnpmパッケージのバージョンは一致している必要があります(例: `v0.1.0`と`0.1.0`)
- ルートの`internationalizer`パッケージは、`internationalizer-darwin-arm64`などのプラットフォーム別パッケージに依存します
-- サポート対象のnpmターゲット:macOS arm64/x64、Linux arm64/x64、Windows x64
+- サポート対象のnpmターゲット: macOS arm64/x64、Linux arm64/x64、Windows x64
- CIでのパッケージ公開には、`NPM_TOKEN`という名前のGitHubシークレットが必要です
-
+
## クイックスタート
-1. プロジェクトのルートに設定ファイルを作成します。
+1. プロジェクトのルートに設定ファイルを作成します:
```yaml
# .internationalizer.yml
@@ -89,30 +89,30 @@ llm:
api_key_env: GOOGLE_AI_STUDIO_API_KEY
```
-2. APIキーを設定します。
+2. APIキーを設定します:
```bash
export GOOGLE_AI_STUDIO_API_KEY=your-ai-studio-key
```
-3. 翻訳対象をプレビューします。
+3. 翻訳対象をプレビューします:
```bash
internationalizer translate --dry-run
```
-4. 翻訳を実行します。
+4. 翻訳を実行します:
```bash
internationalizer translate
```
-5. すべてのロケールを検証します。
+5. すべてのロケールを検証します:
```bash
internationalizer validate
```
-
+
## コマンド
### `translate`
@@ -120,60 +120,60 @@ internationalizer validate
欠落しているキーや古くなったキーを検出し、LLM経由で翻訳します。
```bash
-internationalizer translate # すべてのロケールを翻訳
-internationalizer translate -l fr # フランス語のみ翻訳
-internationalizer translate --dry-run # APIを呼び出さずにプレビュー
-internationalizer translate --adopt-existing # APIを呼び出さずに既存の翻訳をベースライン化
-internationalizer translate --refresh-policy # プロンプト/スタイル/モデルの変更により古くなったエントリを更新
-internationalizer translate --batch-size 20 # より小さいバッチサイズで実行
-internationalizer translate --concurrency 2 # 並行呼び出し数を抑えて実行
+internationalizer translate # translate all locales
+internationalizer translate -l fr # translate French only
+internationalizer translate --dry-run # preview without API calls
+internationalizer translate --adopt-existing # baseline existing translations without API calls
+internationalizer translate --refresh-policy # refresh prompt/style/model-stale entries
+internationalizer translate --batch-size 20 # smaller batches
+internationalizer translate --concurrency 2 # fewer parallel calls
```
-翻訳状態は、欠落(missing)、ソース変更(source-stale)、ポリシー変更(policy-stale)、最新(current)、手動編集済み(manually edited)の状態を個別にレポートするため、手動編集によってソースやポリシーの変更が覆い隠されることはありません。ポリシー変更によって古くなった値はレポートされますが、`--refresh-policy`を指定した場合にのみ再翻訳されます。手動編集された値が自動的に上書きされることは決してありません。レビュー済みの翻訳にマニフェストを導入する場合や、レビュー済みの手動編集を新しいベースラインとして明示的に受け入れる場合は、`--adopt-existing`を使用してください。
+翻訳状態は、欠落(missing)、ソース変更(source-stale)、ポリシー変更(policy-stale)、最新(current)、手動編集済み(manually edited)の状態を個別にレポートするため、手動編集によってソースやポリシーの変更が隠蔽されることはありません。ポリシー変更によって古くなった値はレポートされますが、`--refresh-policy`を指定した場合にのみ再翻訳されます。手動編集された値が自動的に上書きされることは決してありません。レビュー済みの翻訳にマニフェストを導入する場合や、レビュー済みの手動編集を新しいベースラインとして明示的に受け入れる場合は、`--adopt-existing`を使用してください。
### `validate`
-すべてのロケールファイルをソースバンドルと照合します。デフォルトでは、必須ターゲットキーの充足率を検査し、余分なキーを警告として報告します。キーの欠落、補間パラメータの不一致、ICU MessageFormatの構文エラーがある場合は失敗します。
+すべてのロケールファイルをソースバンドルと照合します。デフォルトの検証では構造的な充足率(必要なターゲットキーが存在する割合)を検査し、余分なキーを警告として報告します。キーの欠落、補間パラメータの不一致、無効なICU MessageFormat構造がある場合は失敗します。
```bash
-internationalizer validate # 人間が読みやすい形式で出力
-internationalizer validate --json # 機械可読なJSON形式で出力
-internationalizer validate -q # 終了コードのみ出力
-internationalizer validate --strict # 翻訳品質に関する規則を適用
-internationalizer validate --require-state # マニフェストが最新であることを必須化
+internationalizer validate # human-readable output
+internationalizer validate --json # machine-readable JSON
+internationalizer validate -q # exit code only
+internationalizer validate --strict # enforce translation quality rules
+internationalizer validate --require-state # require current manifest provenance
```
-`--strict`では翻訳済みの割合も報告します。言語表現としての値がソースと同一の場合、用語集にその値全体についてソースとターゲットが完全に同じ項目がない限り、未翻訳と判定されます。`ignore_case`は考慮されますが、長い値の一部に用語集の語が含まれるだけでは除外されません。strictモードでは、余分なキー、ソースと同一の値、補間・HTML・コード・Markdownリンクの構造変更、用語集違反、設定された複数形の不足があると失敗します。
+`--strict`は翻訳済みの充足率も報告します。言語表現としての値がソースと同一の場合、値全体に対してソースとターゲットが完全に一致する項目が用語集に明示的に登録されていない限り、未翻訳とみなされます。`ignore_case`は考慮されますが、より長い値の一部を用語集の語が占めているだけでは除外対象になりません。strictモードでは、余分なキー、ソースと同一の値、補間・HTML・コード・Markdownリンクの構造変更、用語集違反、設定された複数形の不備がある場合に失敗します。
-`--require-state`は、各ターゲットを`.internationalizer.lock`と照合します。キーが未記録の場合や、記録されたソース、翻訳ポリシー、ターゲットのハッシュが古い場合は失敗します。`--strict`と併用できます。
+`--require-state`は、各ターゲットを`.internationalizer.lock`と照合します。キーが未追跡の場合や、記録されたソース、翻訳ポリシー、ターゲットのハッシュが古い場合に失敗します。`--strict`と併用できます。
-人間向けレポートとJSONレポートでは、次の安定した検出コードを使用します。
+人間向けおよびJSONレポートでは、安定した検出コードを使用します:
| コード | 意味 |
| --- | --- |
-| `missing_key` / `extra_key` | ソースとターゲットのキー集合が一致していない |
-| `blank_translation` | 空でないソースに対するstrictモードのターゲットが空 |
-| `source_identical` | 言語表現としての値がstrictモードでもソースと同一 |
-| `protected_structure_mismatch` | 補間、HTML、コード、リンクの構造が変更されている |
+| `missing_key` / `extra_key` | ソースとターゲットのキー集合が異なる |
+| `blank_translation` | 空でないソースに対してstrictモードのターゲットが空 |
+| `source_identical` | strictモードの言語表現の値が未翻訳のまま |
+| `protected_structure_mismatch` | 補間、HTML、コード、リンク構造が変更されている |
| `glossary_violation` | 承認済みのターゲット用語または異表記が見つからない |
-| `plural_form_missing` | 設定されたロケールの複数形が不足している |
+| `plural_form_missing` | 設定されたロケールの複数形が存在しない |
| `icu_message_syntax` | ソースまたはターゲットのICUメッセージが不正 |
-| `icu_argument_mismatch` | ICUの引数名、種類、フォーマッタースタイルが一致していない |
-| `icu_selector_mismatch` | セレクターが一致していない、または複数形カテゴリーがターゲットロケールで無効 |
-| `untracked` | ターゲットに対応するマニフェストレコードがない |
-| `source_stale` | 記録後にソースの内容が変更された |
+| `icu_argument_mismatch` | ICUの引数名、型、またはフォーマッタースタイルが異なる |
+| `icu_selector_mismatch` | セレクターが異なる、または複数形カテゴリーがターゲットロケールで無効 |
+| `untracked` | ターゲットに対応するマニフェストレコードが存在しない |
+| `source_stale` | 記録された翻訳の後にソースの内容が変更された |
| `policy_stale` | 生成プロンプトまたはモデル設定が変更された |
| `target_modified` | ターゲットの内容がマニフェストの記録と異なる |
### `detect`
-使用中のi18nフレームワークを自動検出し、推奨設定を提案します。
+i18nフレームワークを自動検出し、設定を提案します。
```bash
internationalizer detect
```
-サポート対象:react-i18next、next-intl、vue-i18n、プレーンなJSON、Markdownドキュメント。
+サポート対象: react-i18next、next-intl、vue-i18n、プレーンなJSON、Markdownドキュメント。
### `glossary`
@@ -187,27 +187,27 @@ internationalizer glossary remove --locale fr --source "Dashboard"
### `tm`
-翻訳メモリ(過去に翻訳された文字列を保存するJSONLキャッシュ)を管理します。
+翻訳メモリ(過去に翻訳された文字列のJSONLキャッシュ)を管理します。
```bash
-internationalizer tm stats # レコード数を表示
-internationalizer tm export # JSON形式でダンプ出力
-internationalizer tm clear --force # すべてのレコードを削除
+internationalizer tm stats # show record counts
+internationalizer tm export # dump as JSON
+internationalizer tm clear --force # delete all records
```
-
+
## 設定リファレンス
```yaml
# .internationalizer.yml
-# ソース言語(デフォルト:en)
+# Source language (default: en)
source_locale: en
-# 翻訳先言語(必須)
+# Languages to translate into (required)
target_locales: [fr, de, es, ja, yue, zh-CN, zh-TW, ar]
-# 1つ以上のソースからターゲットへのマッピング(必須)。
-# {locale}は設定された各ターゲットロケールに置換されます。
+# One or more source-to-target mappings (required).
+# {locale} is replaced with each configured target locale.
bundles:
- id: app
source: locales/en.json
@@ -218,35 +218,35 @@ bundles:
target: docs/i18n/{locale}.md
format: markdown
-# 下位互換性:source_pathも引き続きlocales/fr.jsonなどの兄弟ファイルへターゲットをマッピングします。
-# 新規プロジェクトではbundlesの使用を推奨します。
+# Backward compatibility: source_path still maps targets to sibling files
+# such as locales/fr.json. Prefer bundles for new projects.
# source_path: locales/en.json
-# LLMプロバイダー設定
+# LLM provider settings
llm:
- # プロバイダー:「anthropic」、「openai」、「gemini」、または「openrouter」(デフォルト:gemini)
+ # Provider: "anthropic", "openai", "gemini", or "openrouter" (default: gemini)
provider: gemini
- # プロバイダーごとのデフォルトモデル名
+ # Model name defaults by provider:
# anthropic: claude-opus-5
- # openai: gpt-5.6-luna(推論強度のデフォルトはmax)
+ # openai: gpt-5.6-luna (reasoning effort defaults to max)
# gemini: gemini-3.8-flash
# openrouter: deepseek/deepseek-v4-pro-0813
model: gemini-3.8-flash
- # APIキーを格納した環境変数名
+ # Environment variable containing the API key
api_key_env: GOOGLE_AI_STUDIO_API_KEY
- # OpenAI互換エンドポイントのベースURL(オプション)
+ # Base URL for OpenAI-compatible endpoints (optional)
# base_url: https://api.openai.com
- # OpenAI GPT-5シリーズ Responses APIの推論強度
- # (OpenAIプロバイダーのデフォルト:max)
+ # OpenAI GPT-5-series Responses API reasoning effort
+ # (default: max for the OpenAI provider)
reasoning_effort: max
- # 特定のターゲットロケールに対するオプションのLLM設定。
- # グローバル設定と同一のプロバイダーを使用する言語オーバーライドは、未指定の項目にグローバル設定を継承します。
- # 異なるプロバイダーを指定した場合、未指定の項目にはそのプロバイダーのデフォルト値が適用されます。
+ # Optional LLM settings for individual target locales. An override using the
+ # global provider inherits unspecified global settings. A different provider
+ # uses that provider's defaults for unspecified settings.
locale_overrides:
yue:
provider: openrouter
@@ -261,61 +261,63 @@ llm:
model: deepseek/deepseek-v4-flash-0731
api_key_env: OPENROUTER_API_KEY
-# LLM呼び出しあたりのキー数(デフォルト:40)
+# Keys per LLM call (default: 40)
batch_size: 40
-# LLMの並行呼び出し数(デフォルト:4)
+# Parallel LLM calls (default: 4)
concurrency: 4
-# ロケール別スタイルガイドMarkdownファイルを格納するディレクトリ(デフォルト:style-guides)
+# Directory containing per-locale style guide Markdown files (default: style-guides)
style_guides_dir: style-guides
-# ロケール別用語集JSONファイルを格納するディレクトリ(デフォルト:glossary)
+# Directory containing per-locale glossary JSON files (default: glossary)
glossary_dir: glossary
-# 翻訳メモリファイルへのパス(デフォルト:.internationalizer/tm.jsonl)
+# Path to translation memory file (default: .internationalizer/tm.jsonl)
tm_path: .internationalizer/tm.jsonl
-# ソース、ポリシー、ターゲット、および来歴情報のバージョン管理状態
-# (デフォルト:.internationalizer.lock。このファイルをコミットしてください)
+# Versioned source, policy, target, and provenance state
+# (default: .internationalizer.lock; commit this file)
manifest_path: .internationalizer.lock
-# 翻訳とstrict検証に関するオプション規則
+# Optional translation and strict-validation rules
validation:
- plural_style: i18next-v4 # ターゲットロケールの複数形を生成して検証
+ plural_style: i18next-v4 # generate and validate target-locale plural forms
```
-ロケール識別子には、`fr`、`pt-BR`、`sr-Latn-RS`など、正しい形式のBCP 47タグを指定してください。正規化すると同一になるターゲットロケールは重複として拒否され、ロケール別のプロバイダーオーバーライドも正規化後の表記で照合されます。上の例では、日本語を含むオーバーライドのないロケールは、グローバルなGemini設定を継承します。
-
-ICU MessageFormatの値は構造として解析されます。単純な引数のほか、`select`、`plural`、`selectordinal`、`number`、`date`、`time`に対応し、メッセージのネスト、複数形のオフセット、数値セレクター、`#`も使用できます。検証では、構文、引数の種類とフォーマッタースタイル、複数形のオフセット、selectの分岐、ターゲットロケールのCLDR複数形カテゴリーを確認します。これらの条件を破るプロバイダー出力は、ロケールファイルや翻訳メモリへ書き込まれる前に拒否されます。
+ロケール識別子には、`fr`、`pt-BR`、`sr-Latn-RS`などの適切な形式のBCP 47タグを使用する必要があります。正規同等なターゲットロケールは重複として拒否され、ロケール固有のプロバイダーオーバーライドも正規同等な表記に一致します。上の例では、オーバーライドのないロケール(日本語を含む)はグローバルのGemini設定を継承します。
-`i18next-v4`を指定すると、認識されたソースの複数形ファミリーが、翻訳時にターゲットロケールのCLDRカテゴリーへ展開されます。ターゲットにしかないカテゴリーでは、ソースファミリーの`_other`値を翻訳テンプレートとして使用します。strict検証ではターゲットに必要なカテゴリーを必須とし、ターゲットロケールで使用しないソース側だけのカテゴリーは任意として扱います。
+ICU MessageFormatの値は構造的に解析されます。単純な引数、`select`、`plural`、`selectordinal`、`number`、`date`、`time`がサポートされており、ネストされたメッセージ、複数形のオフセット、厳密な数値セレクター、`#`が含まれます。検証では、構文、引数の型とフォーマッタースタイル、複数形のオフセット、select分岐の同一性、ターゲットロケールのCLDR複数形カテゴリーをチェックします。これらの不変条件に違反するプロバイダー出力は、ロケールファイルや翻訳メモリレコードに書き込まれる前に拒否されます。
+`i18next-v4`を使用すると、認識されたソースの複数形ファミリーが翻訳中にターゲットロケールのCLDRカテゴリーに展開されます。ターゲットにのみ存在するカテゴリーは、ソースファミリーの`_other`値を翻訳テンプレートとして使用します。strict検証ではこれらのターゲットカテゴリーが必須となります。ターゲットロケールで使用されないソース固有のカテゴリーは任意です。
+
## スタイルガイド
-スタイルガイドは、LLMの翻訳プロンプトに挿入されるMarkdownファイルです。トーン、丁寧さの度合い、タイポグラフィ、その他の言語固有の規則を制御します。
+スタイルガイドは、LLM翻訳プロンプトに挿入されるMarkdownファイルです。トーン、丁寧さの度合い、タイポグラフィ、その他の言語固有の慣例を制御します。
```
style-guides/
- _conventions.md # すべての言語に共通のルール
- fr.md # フランス語固有のルール
- ja.md # 日本語固有のルール
- ar.md # アラビア語固有のルール
+ _conventions.md # shared rules for all languages
+ fr.md # French-specific rules
+ ja.md # Japanese-specific rules
+ ar.md # Arabic-specific rules
```
-### 共通規則 (`_conventions.md`)
+### 共通規則(`_conventions.md`)
-すべての言語に適用されるルールを定義します。補間構文、HTMLの保持、文字列種別ごとの規則(ボタン、ラベル、エラーメッセージなど)を指定します。
+すべての言語に適用されるルールを定義します: 補間構文、HTMLの保持、文字列種別の規則(ボタン、ラベル、エラーなど)。
-### 言語別ガイド (`{locale}.md`)
+### 言語別ガイド(`{locale}.md`)
-言語固有のルールを定義します。丁寧さの度合い(「tu」と「vous」の使い分けなど)、句読点(ギュメ、逆疑問符など)、複数形、日付や数値の書式、用語集を指定します。
+言語固有のルールを定義します: 敬体・常体の使い分け(tuとvousなど)、句読点(ギュメ、逆疑問符)、複数形、日付・数値の書式、用語集。
-実際の構成例については、[`examples/react-app/style-guides/`](../../examples/react-app/style-guides/)を参照してください。
+スタイルガイドは永続的なポリシー入力であり、生成された出力ではありません。Internationalizerはこれらを読み取りますが、書き換えることはありません。その内容は用語集やプロンプト規約とは別にハッシュ化されるため、アプリケーションコードの変更によって翻訳が古くなることはありません。ガイドを編集すると、そのロケールは意図的にポリシー再確認の対象としてマークされます。内部プロンプトの文言を変更しても、プロンプト規約のバージョンが変わらない限り、古くなったとはみなされません。
+動作例については、[`examples/react-app/style-guides/`](../../examples/react-app/style-guides/)を参照してください。
+
## 用語集の形式
-用語集ファイルは、`{glossary_dir}/{locale}.json`に配置するJSON配列です。
+用語集ファイルは、`{glossary_dir}/{locale}.json`に格納されるJSON配列です:
```json
[
@@ -330,78 +332,80 @@ style-guides/
]
```
-`variants`には、承認済みの別表記を指定します。`enforcement`には`error`または`warning`を指定でき、省略時は`error`です。用語は対照表としてLLMプロンプトに挿入され、アプリケーション全体で一貫した翻訳に使用されます。`{"source":"API","target":"API"}`のようにソースとターゲットが完全に同じ項目を登録すると、その値全体はstrict検証の未翻訳判定から除外されます。長い値の一部に`API`が含まれるだけでは除外されません。
-
+`variants`には、承認されたその他のターゲット表現をリストします。`enforcement`には`error`、`warning`を指定でき、省略した場合はデフォルトのerror動作になります。用語は用語集テーブルとしてLLMプロンプトに挿入され、アプリケーション全体での一貫した翻訳を保証します。`{"source":"API","target":"API"}`のような完全一致エントリは、その完全なソース同一値をstrictモードの未翻訳検出から除外します。単に`API`を含むだけのより長い値は除外されません。
+
## 翻訳メモリ
-翻訳メモリはJSONLファイル(1行につき1件のJSONレコード)として保存されます。各レコードには以下の情報が含まれます。
+翻訳メモリはJSONLファイル(1行に1つのJSONレコード)として保存されます。各レコードには以下が含まれます:
-- バンドル、キー、ソース値、翻訳後の値、正規化されたターゲットロケール
-- ソースと翻訳ポリシーのハッシュ
-- 翻訳に使用したプロバイダーとモデル
+- バンドル、キー、ソース値、翻訳値、正規化されたターゲットロケール
+- ソース、スタイルガイド、用語集、プロンプト規約、および統合ポリシーの各ハッシュ
+- 翻訳を生成したプロバイダーおよびモデル
- タイムスタンプ
-次回以降の実行時には、ソースとポリシーのハッシュが同じ文字列がLLMを呼び出さずにキャッシュから取得されます。デフォルトの保存先はGit管理から除外された`.internationalizer/`ディレクトリ内なので、ローカルキャッシュとして扱われます。翻訳メモリを意図的に共有する場合は、`tm_path`をGit管理対象のパスへ変更してください。レビュー可能な`.internationalizer.lock`マニフェストは別途バージョン管理されます。
-
+以降の実行では、ソースとポリシーのハッシュが同じ文字列は、LLMを呼び出さずにキャッシュから提供されます。デフォルトのパスは無視対象の`.internationalizer/`ディレクトリ配下にあるため、ローカルキャッシュのまま保持されます。プロジェクトで意図的に翻訳メモリを共有する場合は、`tm_path`を追跡対象の場所に変更してください。レビュー可能な`.internationalizer.lock`マニフェストは個別にバージョン管理されます。
+
## サポート対象の形式
-| 形式 | 拡張子 | 処理モード |
-|--------|-----------|------|
+| 形式 | 拡張子 | モード |
+| --- | --- | --- |
| JSON | `.json` | キーバリュー(ネスト対応、ドット記法による平坦化) |
-| YAML | `.yml`, `.yaml` | キーバリュー(コメントと記述順序を保持) |
-| Markdown | `.md`, `.mdx` | ドキュメント全体の翻訳 |
+| YAML | `.yml`, `.yaml` | キーバリュー(コメントと順序を保持) |
+| Markdown | `.md`, `.mdx` | プリアンブルおよびH2レベルのセクション |
+Markdownターゲットには、H2セクションの前に不可視の`internationalizer:unit`コメントが含まれます。これらの安定したマーカーにより、Internationalizerは関係のないセクションを再翻訳することなく、1つのソースセクションを追加、移動、編集できます。マーカーのない既存のドキュメントには、次回の正常な更新時にマーカーが付与されます。
+
## プロジェクトタイプの検出
-`internationalizer detect`は、以下の項目を検査してi18n構成を特定します。
+`internationalizer detect`は、以下をチェックしてi18n設定を特定します:
-- `package.json`内のreact-i18next、next-intl、vue-i18nなどの依存関係
-- 一般的なロケール配置パターンに一致するディレクトリ構造
+- `package.json`の依存関係におけるreact-i18next、next-intl、vue-i18n
+- 一般的なロケールパターンに一致するディレクトリ構造
- ファイル拡張子および命名規則
-
+
## アーキテクチャ
```
-cmd/internationalizer/ CLIエントリポイントと各コマンドの定義
+cmd/internationalizer/ CLI entry point and command definitions
internal/
- config/ デフォルト値を適用したYAML設定の読み込み
- detect/ プロジェクトタイプの自動検出
- formats/ フォーマットパーサー(JSON、YAML、Markdown)
- glossary/ ロケール別用語集の管理
- llm/ LLMプロバイダーのインターフェースと実装
- anthropic.go Anthropic Claudeバックエンド
- openai.go OpenAI / 互換エンドポイントバックエンド
- gemini.go Google AI Studio経由のGeminiバックエンド
- OpenRouterはカスタムbase_urlを指定してopenai.goを使用
- locale/ BCP 47ロケールIDとCLDR複数形カテゴリー
- message/ ICU MessageFormatのパーサーと構造比較
- policy/ 安定した翻訳ポリシーのハッシュ化
- state/ バージョン管理される翻訳マニフェスト
- styleguide/ スタイルガイドの読み込み
- tm/ JSONL形式の翻訳メモリ
- translate/ 翻訳処理のオーケストレーター
- validate/ ロケールの検証と差分抽出
+ config/ YAML config loading with defaults
+ detect/ Project type auto-detection
+ formats/ Format parsers (JSON, YAML, Markdown)
+ glossary/ Per-locale glossary management
+ llm/ LLM provider interface + implementations
+ anthropic.go Anthropic Claude backend
+ openai.go OpenAI / compatible backend
+ gemini.go Google Gemini via AI Studio backend
+ OpenRouter uses openai.go with custom base_url
+ locale/ BCP 47 identity and CLDR plural categories
+ message/ ICU MessageFormat parser and structural comparison
+ policy/ Stable translation-policy hashing
+ state/ Versioned translation manifest
+ styleguide/ Style guide loader
+ tm/ JSONL translation memory
+ translate/ Translation orchestrator
+ validate/ Locale validation and diffing
```
-
+
## 代替ツールとの比較
| 機能 | Internationalizer | i18next | Crowdin | 汎用LLM |
-|---------|------------------|---------|---------|-------------|
+| --- | --- | --- | --- | --- |
| LLMを活用した翻訳 | はい | いいえ | 一部対応 | はい |
| 言語ごとのスタイルガイド | はい | いいえ | いいえ | いいえ |
| 用語集の強制適用 | はい | いいえ | はい | いいえ |
| 翻訳メモリ | はい | いいえ | はい | いいえ |
-| CLI / ローカル環境での実行 | はい | 該当なし | いいえ | 手動 |
-| Git管理に適したファイル形式 | はい | はい | 一部対応 | 手動 |
-| SaaSへの依存なし | はい | はい | いいえ | ツールによる |
-| オープンソース(AGPL-3.0) | はい | はい | いいえ | ツールによる |
-
+| CLI / ローカル実行 | はい | 該当なし | いいえ | 手動 |
+| Gitに適したファイル | はい | はい | 一部対応 | 手動 |
+| SaaSへの依存なし | はい | はい | いいえ | 状況による |
+| オープンソース(AGPL-3.0) | はい | はい | いいえ | 状況による |
+
## ライセンス
[AGPL-3.0](../../LICENSE)
-依存関係に関する通知は、[THIRD_PARTY_NOTICES.md](../../THIRD_PARTY_NOTICES.md)を参照してください。
-
+依存関係の通知については、[THIRD_PARTY_NOTICES.md](../../THIRD_PARTY_NOTICES.md)を参照してください。
+
## コントリビューション
-開発環境のセットアップおよびガイドラインについては、[CONTRIBUTING.md](../../CONTRIBUTING.md)を参照してください。すべてのコントリビューションにはDCO(開発者原産性証明)への署名(サインオフ)が必須です。
+開発のセットアップとガイドラインについては、[CONTRIBUTING.md](../../CONTRIBUTING.md)を参照してください。すべてのコントリビューションにはDCOのサインオフが必要です。
diff --git a/docs/i18n/ko.md b/docs/i18n/ko.md
index 953e09c..ddaa62b 100644
--- a/docs/i18n/ko.md
+++ b/docs/i18n/ko.md
@@ -6,55 +6,57 @@
# Internationalizer
-소프트웨어 프로젝트를 위한 AI 네이티브 국제화 파이프라인입니다. LLM을 사용하여 i18n 파일을 번역, 검증 및 관리합니다.
+소프트웨어 프로젝트를 위한 AI 네이티브 국제화 파이프라인입니다. LLM을 활용하여 i18n 파일을 번역, 검증 및 관리합니다.
[](https://github.com/Tom-R-Main/Internationalizer/actions/workflows/ci.yml)
-[](LICENSE)
+[](../../LICENSE)
-العربية · বাংলা · Deutsch · Español · Français · हिन्दी · Indonesia · 日本語 · ਪੰਜਾਬੀ · Português · Русский · తెలుగు
ไทย · Українська · 粵語 · 简体中文 · 繁體中文
+العربية · বাংলা · Čeština · Dansk · Deutsch · Ελληνικά · Español · Suomi · Français · עברית · हिन्दी · Indonesia · Italiano · 日本語 · 한국어 · Bahasa Melayu
Nederlands · ਪੰਜਾਬੀ · Polski · Português · Română · Русский · Svenska · తెలుగు · ไทย · Türkçe · Українська · Tiếng Việt · 粵語 · 简体中文 · 繁體中文
---
-## 왜 Internationalizer를 사용해야 할까요?
+
+## 왜 Internationalizer인가요?
-대부분의 i18n 도구는 런타임 라이브러리(i18next, react-intl)이거나 키 관리 SaaS 플랫폼(Crowdin, Lokalise)입니다. 하지만 이들 중 어느 것도 실제 번역 문제를 제대로 해결하지 못합니다.
+대부분의 i18n 도구는 런타임 라이브러리(i18next, react-intl)이거나 키 관리 SaaS 플랫폼(Crowdin, Lokalise)입니다. 두 유형 모두 실제 번역 문제를 만족스럽게 해결하지 못합니다.
-- **수동 번역**은 지원 언어가 몇 개만 넘어가도 확장하기 어렵습니다.
-- **기계 번역 API**(Google Translate, DeepL)는 용어, 어조 및 UI 규칙을 무시합니다.
-- **일반적인 LLM 번역**은 더 나은 결과를 보여주지만, 용어집과 스타일 가이드가 없으면 일관성 없는 결과를 얻게 됩니다.
+- **수동 번역**은 지원 언어가 몇 개만 넘어가도 확장할 수 없습니다.
+- **기계 번역 API**(Google Translate, DeepL)는 제품 전용 용어, 어조, UI 규칙을 반영하지 못합니다.
+- **일반 LLM 번역**은 상대적으로 더 우수하지만, 용어집과 스타일 가이드가 없으면 일관성 없는 결과를 산출합니다.
-Internationalizer는 다릅니다. LLM 번역과 다음 기능을 결합한 **CLI 파이프라인**입니다.
+Internationalizer는 다릅니다. LLM 번역에 다음 핵심 기능을 결합한 **CLI 파이프라인**입니다.
-- **언어별 용어집** — 앱 전체에서 일관된 용어를 적용합니다.
-- **언어별 스타일 가이드** — 어조, 격식, 복수형 및 타이포그래피를 제어합니다.
-- **번역 메모리** — 변경되지 않은 문자열을 건너뛰어 API 호출 비용을 절약합니다.
-- **키 검증** — 배포 전에 누락된 번역과 보간 불일치를 찾아냅니다.
+- **언어별 용어집** — 애플리케이션 전반에서 일관된 전문 용어를 강제합니다.
+- **언어별 스타일 가이드** — 어조, 격식 수준, 복수형 처리, 타이포그래피 규칙을 제어합니다.
+- **번역 메모리** — 변경되지 않은 문자열을 건너뛰어 API 호출 비용을 절감합니다.
+- **결정론적 검증** — 누락되거나 불필요한 키, 보호된 구조 변형, 용어집 위반, 복수형 및 ICU 구문 오류를 배포 전에 선제적으로 감지합니다.
+
## 설치
-npm에서 설치합니다.
+npm을 통해 설치할 수 있습니다.
```bash
npm install -g internationalizer
```
-또는 전역 설치 없이 실행합니다.
+전역 설치 없이 바로 실행할 수도 있습니다.
```bash
npx internationalizer --help
```
-npm 패키지는 플랫폼별 선택적 종속성을 통해 npm에서 일치하는 사전 빌드된 바이너리를 설치합니다.
+npm 패키지는 플랫폼별 선택적 종속성(optional dependencies)을 통해 환경에 맞는 사전 빌드 바이너리를 설치합니다.
-Go를 사용하여 설치합니다.
+Go를 사용하여 설치할 수도 있습니다.
```bash
go install github.com/Tom-R-Main/Internationalizer/cmd/internationalizer@latest
```
-또는 소스에서 빌드합니다.
+소스 코드에서 직접 빌드하는 방법도 지원합니다.
```bash
git clone https://github.com/Tom-R-Main/Internationalizer.git
@@ -62,13 +64,15 @@ cd Internationalizer
go build -o internationalizer ./cmd/internationalizer
```
+
## npm 패키지
-- Git 태그와 npm 패키지 버전은 일치해야 합니다. (예: `v0.1.0` 및 `0.1.0`)
-- 루트 `internationalizer` 패키지는 `internationalizer-darwin-arm64`와 같은 플랫폼 패키지에 종속됩니다.
-- 지원되는 npm 대상: macOS arm64/x64, Linux arm64/x64, Windows x64
-- CI 게시에 `NPM_TOKEN`이라는 GitHub 시크릿이 필요합니다.
+- Git 태그와 npm 패키지 버전은 반드시 일치해야 합니다(예: `v0.1.0` 및 `0.1.0`).
+- 루트 `internationalizer` 패키지는 `internationalizer-darwin-arm64`와 같은 플랫폼별 바이너리 패키지에 종속됩니다.
+- 지원되는 npm 플랫폼: macOS arm64/x64, Linux arm64/x64, Windows x64
+- CI 자동 배포를 구성하려면 `NPM_TOKEN`이라는 이름의 GitHub Secret이 필요합니다.
+
## 빠른 시작
1. 프로젝트 루트에 구성 파일을 생성합니다.
@@ -77,7 +81,11 @@ go build -o internationalizer ./cmd/internationalizer
# .internationalizer.yml
source_locale: en
target_locales: [fr, de, es, ja]
-source_path: locales/en.json
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
llm:
provider: gemini
@@ -85,13 +93,13 @@ llm:
api_key_env: GOOGLE_AI_STUDIO_API_KEY
```
-2. API 키를 설정합니다.
+2. API 키 환경 변수를 설정합니다.
```bash
export GOOGLE_AI_STUDIO_API_KEY=your-ai-studio-key
```
-3. 번역될 내용을 미리 봅니다.
+3. 번역될 대상을 미리 확인합니다.
```bash
internationalizer translate --dry-run
@@ -103,49 +111,78 @@ internationalizer translate --dry-run
internationalizer translate
```
-5. 모든 로캘을 검증합니다.
+5. 모든 로캘 파일을 검증합니다.
```bash
internationalizer validate
```
+
## 명령어
### `translate`
-누락된 키를 찾아 LLM을 통해 번역합니다.
+누락되었거나 변경이 필요한 키를 찾아 LLM으로 번역합니다.
```bash
internationalizer translate # translate all locales
internationalizer translate -l fr # translate French only
internationalizer translate --dry-run # preview without API calls
+internationalizer translate --adopt-existing # baseline existing translations without API calls
+internationalizer translate --refresh-policy # refresh prompt/style/model-stale entries
internationalizer translate --batch-size 20 # smaller batches
internationalizer translate --concurrency 2 # fewer parallel calls
```
+번역 상태는 누락(missing), 소스 변경(source-stale), 정책 변경(policy-stale), 최신 상태(current), 수동 편집(manually edited) 조건을 각각 독립적으로 추적하고 보고합니다. 따라서 수동 편집 내역이 원본 소스 변경이나 정책 변경 사항을 은폐하지 않습니다. 정책 변경으로 인해 갱신이 필요한 항목은 기본 실행 시 상태만 보고되며 `--refresh-policy` 플래그를 지정할 때만 다시 번역됩니다. 수동으로 편집된 값은 자동으로 덮어쓰지 않습니다. 이미 검토가 완료된 기존 번역본에 매니페스트를 처음 도입하거나, 수동 편집 사항을 새로운 기준선으로 명시적으로 수용할 때는 `--adopt-existing` 옵션을 사용하세요.
+
### `validate`
-모든 로캘 파일에서 누락된 키, 추가된 키 및 보간 불일치가 있는지 확인합니다.
+모든 로캘 파일을 소스 번들과 비교하여 검증합니다. 기본 검증 모드는 구조적 커버리지(필수 타깃 키의 존재 비율)를 확인하고, 불필요한 추가 키는 경고로 보고하며, 키 누락, 보간(interpolation) 불일치, 유효하지 않은 ICU MessageFormat 구조가 발견되면 검증 실패로 처리합니다.
```bash
internationalizer validate # human-readable output
internationalizer validate --json # machine-readable JSON
internationalizer validate -q # exit code only
+internationalizer validate --strict # enforce translation quality rules
+internationalizer validate --require-state # require current manifest provenance
```
+`--strict` 플래그는 번역 완료 커버리지도 함께 검증합니다. 소스와 완전히 동일한 언어적 텍스트는 용어집에 정확히 동일한 소스 및 타깃 텍스트가 전체 값 단위로 등록되어 있지 않는 한 미번역 항목으로 간주됩니다(`ignore_case` 설정은 반영되지만, 긴 문장 안에 해당 용어집 단어가 포함되어 있다는 사실만으로는 예외 처리가 적용되지 않습니다). 엄격(strict) 모드는 추가 키, 소스와 동일한 미번역 값, 보간/HTML/코드/Markdown 링크 구조 변형, 용어집 위반, 설정된 복수형 미준수 사항이 감지되면 즉시 실패 처리합니다.
+
+`--require-state` 플래그는 각 타깃 파일이 `.internationalizer.lock` 상태와 일치하는지 추적 상태를 확인합니다. 매니페스트에 등록되지 않은 키가 존재하거나 기록된 소스, 번역 정책, 타깃 해시가 최신 상태가 아니면 실패 처리됩니다. 이 옵션은 `--strict`와 결합하여 실행할 수 있습니다.
+
+사람이 읽을 수 있는 보고서와 JSON 출력 모두 안정적인 검증 코드(finding code)를 사용합니다.
+
+| 코드 | 의미 |
+| --- | --- |
+| `missing_key` / `extra_key` | 소스와 타깃의 키 세트가 서로 일치하지 않습니다. |
+| `blank_translation` | 비어 있지 않은 소스에 대해 엄격 모드 타깃 값이 비어 있습니다. |
+| `source_identical` | 엄격 모드에서 언어적 텍스트가 번역되지 않은 채 원본과 동일하게 유지되었습니다. |
+| `protected_structure_mismatch` | 보간 변수, HTML, 인라인 코드 또는 링크 구조가 원본과 다릅니다. |
+| `glossary_violation` | 승인된 타깃 용어 또는 대체 표기가 사용되지 않았습니다. |
+| `plural_form_missing` | 로캘에 설정된 필수 복수형이 누락되었습니다. |
+| `icu_message_syntax` | 소스 또는 타깃의 ICU 메시지 문법 오류가 존재합니다. |
+| `icu_argument_mismatch` | ICU 인수의 이름, 타입 또는 포맷터 스타일이 다릅니다. |
+| `icu_selector_mismatch` | 선택자(selector)가 다르거나 타깃 로캘에 적합하지 않은 복수형 범주가 사용되었습니다. |
+| `untracked` | 타깃 키에 대한 매니페스트 기록이 존재하지 않습니다. |
+| `source_stale` | 기록된 번역 시점 이후 원본 소스 내용이 변경되었습니다. |
+| `policy_stale` | 생성된 프롬프트 또는 모델 설정이 변경되었습니다. |
+| `target_modified` | 타깃 콘텐츠가 매니페스트에 기록된 내용과 다릅니다. |
+
### `detect`
-i18n 프레임워크를 자동 감지하고 구성을 제안합니다.
+프로젝트에서 사용 중인 i18n 프레임워크를 자동으로 감지하고 최적의 구성을 제안합니다.
```bash
internationalizer detect
```
-지원 항목: react-i18next, next-intl, vue-i18n, 일반 JSON, 마크다운 문서.
+지원 프레임워크: react-i18next, next-intl, vue-i18n, 일반 JSON, Markdown 문서.
### `glossary`
-번역 중에 적용되는 언어별 용어집을 관리합니다.
+번역 시 엄격하게 적용할 언어별 용어집 항목을 관리합니다.
```bash
internationalizer glossary list --locale fr
@@ -155,7 +192,7 @@ internationalizer glossary remove --locale fr --source "Dashboard"
### `tm`
-번역 메모리(이전에 번역된 문자열의 JSONL 캐시)를 관리합니다.
+이전에 번역된 문자열을 캐시하는 번역 메모리(JSONL)를 관리합니다.
```bash
internationalizer tm stats # show record counts
@@ -163,6 +200,7 @@ internationalizer tm export # dump as JSON
internationalizer tm clear --force # delete all records
```
+
## 구성 참조
```yaml
@@ -172,10 +210,23 @@ internationalizer tm clear --force # delete all records
source_locale: en
# Languages to translate into (required)
-target_locales: [fr, de, es, ja, zh-CN, ar]
-
-# Path to the source locale file (required)
-source_path: locales/en.json
+target_locales: [fr, de, es, ja, yue, zh-CN, zh-TW, ar]
+
+# One or more source-to-target mappings (required).
+# {locale} is replaced with each configured target locale.
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
+ - id: docs
+ source: README.md
+ target: docs/i18n/{locale}.md
+ format: markdown
+
+# Backward compatibility: source_path still maps targets to sibling files
+# such as locales/fr.json. Prefer bundles for new projects.
+# source_path: locales/en.json
# LLM provider settings
llm:
@@ -184,7 +235,7 @@ llm:
# Model name defaults by provider:
# anthropic: claude-opus-5
- # openai: gpt-5.6-luna
+ # openai: gpt-5.6-luna (reasoning effort defaults to max)
# gemini: gemini-3.8-flash
# openrouter: deepseek/deepseek-v4-pro-0813
model: gemini-3.8-flash
@@ -195,6 +246,27 @@ llm:
# Base URL for OpenAI-compatible endpoints (optional)
# base_url: https://api.openai.com
+ # OpenAI GPT-5-series Responses API reasoning effort
+ # (default: max for the OpenAI provider)
+ reasoning_effort: max
+
+ # Optional LLM settings for individual target locales. An override using the
+ # global provider inherits unspecified global settings. A different provider
+ # uses that provider's defaults for unspecified settings.
+ locale_overrides:
+ yue:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-CN:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-TW:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+
# Keys per LLM call (default: 40)
batch_size: 40
@@ -209,11 +281,26 @@ glossary_dir: glossary
# Path to translation memory file (default: .internationalizer/tm.jsonl)
tm_path: .internationalizer/tm.jsonl
+
+# Versioned source, policy, target, and provenance state
+# (default: .internationalizer.lock; commit this file)
+manifest_path: .internationalizer.lock
+
+# Optional translation and strict-validation rules
+validation:
+ plural_style: i18next-v4 # generate and validate target-locale plural forms
```
+로캘 식별자는 `fr`, `pt-BR`, `sr-Latn-RS`와 같이 표준 BCP 47 형식이어야 합니다. 정규 동등(canonical-equivalent)한 타깃 로캘은 중복으로 처리되어 거부되며, 로캘별 공급자 재정의도 정규 동등 표기 기준으로 매칭됩니다. 위 예시 설정에서 일본어를 포함해 별도의 재정의가 지정되지 않은 로캘은 전역 Gemini 설정을 상속받습니다.
+
+ICU MessageFormat 값은 구문 구조 단위로 파싱됩니다. 일반 인수 및 `select`, `plural`, `selectordinal`, `number`, `date`, `time`이 지원되며 중첩 메시지, 복수형 오프셋, 정밀 숫자 선택자, `#` 기호 처리도 지원합니다. 검증 과정에서는 구문, 인수 타입 및 포맷터 스타일, 복수형 오프셋, select 분기 일치 여부, 타깃 로캘의 CLDR 복수형 범주 유효성을 평가합니다. 이러한 불변 조건을 위반하는 공급자 응답은 로캘 파일이나 번역 메모리에 기록되기 전에 거부됩니다.
+
+`i18next-v4` 설정 시, 번역 과정에서 인식된 원본 소스의 복수형 그룹이 타깃 로캘에 정의된 CLDR 범주로 확장됩니다. 타깃 언어에만 존재하는 복수형 범주는 소스 그룹의 `_other` 값을 번역 템플릿으로 사용합니다. 엄격 검증 모드에서는 이러한 타깃 범주를 필수로 요구하며, 소스 언어에만 있고 타깃 언어에는 사용되지 않는 범주는 선택 사항으로 취급됩니다.
+
+
## 스타일 가이드
-스타일 가이드는 LLM 번역 프롬프트에 주입되는 마크다운 파일입니다. 어조, 격식, 타이포그래피 및 기타 언어별 규칙을 제어합니다.
+스타일 가이드는 LLM 번역 프롬프트에 직접 주입되는 Markdown 파일입니다. 번역 어조, 격식 수준, 문장 부호 규칙, 기타 언어별 세부 규약을 제어합니다.
```
style-guides/
@@ -225,58 +312,69 @@ style-guides/
### 공통 규칙 (`_conventions.md`)
-모든 언어에 적용되는 규칙을 정의합니다. 보간 구문, HTML 보존, 문자열 유형 규칙(버튼, 레이블, 오류 등)이 포함됩니다.
+모든 언어에 공통으로 적용되는 규칙을 정의합니다. 보간 변수 구문, HTML 태그 보존, 문자열 유형별 규약(버튼, 레이블, 오류 메시지 등)이 포함됩니다.
### 언어별 가이드 (`{locale}.md`)
-언어별 규칙을 정의합니다. 격식 수준(예: 반말과 존댓말), 문장 부호(예: 겹화살괄호, 역물음표), 복수형, 날짜/숫자 형식 및 용어집이 포함됩니다.
+특정 언어에 특화된 규칙을 정의합니다. 대화 상대에 따른 격식 수준(예: tu vs. vous), 문장 부호(길레메 따옴표, 역물음표 등), 복수형 규칙, 날짜 및 숫자 표기법, 용어집 등이 포함됩니다.
-실제 작동 예시는 [`examples/react-app/style-guides/`](examples/react-app/style-guides/)를 참조하세요.
+스타일 가이드는 시스템이 생성하는 산출물이 아니라 지속적으로 관리되는 정책 입력값입니다. Internationalizer는 이 파일들을 읽기만 하며 절대 임의로 수정하지 않습니다. 스타일 가이드 내용은 용어집 및 프롬프트 규약과 별도의 해시로 관리되므로, 일반 애플리케이션 코드가 변경되더라도 기존 번역이 정책 만료 상태로 바뀌지 않습니다. 스타일 가이드를 직접 편집하면 의도적으로 해당 로캘의 정책 재검토 플래그가 설정되지만, 프롬프트 규약 버전이 올라가지 않는 한 내부 프롬프트 문구가 약간 변경되는 것만으로는 정책 만료 상태가 발생하지 않습니다.
+실제 동작하는 예시는 [`examples/react-app/style-guides/`](../../examples/react-app/style-guides/) 디렉터리에서 확인하실 수 있습니다.
+
+
## 용어집 형식
-용어집 파일은 `{glossary_dir}/{locale}.json`에 저장되는 JSON 배열입니다.
+용어집 파일은 `{glossary_dir}/{locale}.json` 경로에 JSON 배열 형태로 저장됩니다.
```json
[
{
"source": "Dashboard",
"target": "Tableau de bord",
+ "variants": ["Panneau de contrôle"],
+ "enforcement": "error",
"ignore_case": false,
"whole_word": true
}
]
```
-용어는 용어 표 형태로 LLM 프롬프트에 주입되어 애플리케이션 전체에서 주요 용어가 일관되게 번역되도록 보장합니다.
+`variants` 필드는 타깃 언어로 허용 가능한 대체 표기 목록을 지정합니다. `enforcement` 필드는 `error` 또는 `warning`으로 지정할 수 있으며 생략 시 기본 동작인 error로 처리됩니다. 등록된 용어는 용어집 테이블 형태로 LLM 프롬프트에 주입되어 애플리케이션 전반에서 일관된 번역을 보장합니다. `{"source":"API","target":"API"}`와 같이 원본과 번역이 동일한 완전 일치 항목을 등록하면 해당 텍스트 전체가 원본과 일치하더라도 엄격 모드에서 미번역 오류로 보고되지 않는 예외 처리를 받습니다. 단, 단순히 `API`라는 단어를 문장 일부로 포함하고 있는 더 긴 문자열에는 이러한 예외가 적용되지 않습니다.
+
## 번역 메모리
-번역 메모리는 JSONL 파일(줄당 하나의 JSON 레코드)로 저장됩니다. 각 레코드에는 다음이 포함됩니다.
+번역 메모리는 한 줄에 하나의 JSON 레코드가 기록되는 JSONL 파일 형태로 저장됩니다. 각 레코드에는 다음 정보가 포함됩니다.
-- 소스 키 및 값
-- 번역된 값
-- 소스 값의 SHA-256 해시
+- 번들 식별자, 키, 원본 소스 값, 번역된 값, 표준 타깃 로캘 태그
+- 소스, 스타일 가이드, 용어집, 프롬프트 규약 및 결합 정책 해시
+- 해당 번역을 생성한 LLM 공급자 및 모델 명칭
- 타임스탬프
-이후 실행 시 변경되지 않은 문자열은 LLM을 호출하지 않고 TM 캐시에서 제공되므로 시간과 API 비용이 모두 절약됩니다. TM 파일은 Git과 호환되며 로캘 파일과 함께 커밋할 수 있습니다.
+이후 번역을 재실행할 때 원본 소스와 정책 해시가 일치하는 문자열은 LLM API를 다시 호출하지 않고 캐시에서 즉시 가져옵니다. 기본 경로는 버전 관리에서 제외되는 `.internationalizer/` 디렉터리 하위에 위치하여 로컬 캐시 역할을 합니다. 프로젝트 팀 전체가 번역 메모리를 공유하려는 경우 `tm_path`를 버전 관리 대상 경로로 설정하세요. 검토 가능한 매니페스트 파일인 `.internationalizer.lock`은 이와 별도로 버전 관리됩니다.
+
## 지원되는 형식
| 형식 | 확장자 | 모드 |
-|--------|-----------|------|
-| JSON | `.json` | 키-값 (중첩, 점 표기법 평탄화) |
+| --- | --- | --- |
+| JSON | `.json` | 키-값 (중첩 객체 및 점 표기법 평탄화 지원) |
| YAML | `.yml`, `.yaml` | 키-값 (주석 및 순서 보존) |
-| Markdown | `.md`, `.mdx` | 전체 문서 번역 |
+| Markdown | `.md`, `.mdx` | 프리앰블 및 H2 단위 섹션 분할 |
+
+Markdown 타깃 파일에는 H2 섹션 직전에 보이지 않는 `internationalizer:unit` 주석이 삽입됩니다. 이러한 안정적 마커를 통해 Internationalizer는 관련 없는 다른 섹션을 다시 번역하지 않고도 변경된 소스 섹션만 개별적으로 추가, 이동, 편집할 수 있습니다. 마커가 없는 기존 문서의 경우 다음 성공적인 업데이트 시 자동으로 마커가 주입됩니다.
+
## 프로젝트 유형 감지
-`internationalizer detect`는 다음을 확인하여 i18n 설정을 식별합니다.
+`internationalizer detect` 명령어는 다음 항목을 확인하여 프로젝트의 i18n 구성을 자동으로 식별합니다.
-- react-i18next, next-intl 또는 vue-i18n에 대한 `package.json` 종속성
+- `package.json`의 react-i18next, next-intl, vue-i18n 종속성 여부
- 일반적인 로캘 패턴과 일치하는 디렉터리 구조
- 파일 확장자 및 명명 규칙
+
## 아키텍처
```
@@ -291,30 +389,38 @@ internal/
openai.go OpenAI / compatible backend
gemini.go Google Gemini via AI Studio backend
OpenRouter uses openai.go with custom base_url
+ locale/ BCP 47 identity and CLDR plural categories
+ message/ ICU MessageFormat parser and structural comparison
+ policy/ Stable translation-policy hashing
+ state/ Versioned translation manifest
styleguide/ Style guide loader
tm/ JSONL translation memory
translate/ Translation orchestrator
validate/ Locale validation and diffing
```
-## 대안과의 비교
+
+## 다른 도구와의 비교
| 기능 | Internationalizer | i18next | Crowdin | 일반 LLM |
-|---------|------------------|---------|---------|-------------|
-| LLM 기반 번역 | 예 | 아니요 | 부분적 | 예 |
+| --- | --- | --- | --- | --- |
+| LLM 기반 번역 | 예 | 아니요 | 부분 지원 | 예 |
| 언어별 스타일 가이드 | 예 | 아니요 | 아니요 | 아니요 |
-| 용어집 적용 | 예 | 아니요 | 예 | 아니요 |
+| 용어집 강제 적용 | 예 | 아니요 | 예 | 아니요 |
| 번역 메모리 | 예 | 아니요 | 예 | 아니요 |
| CLI / 로컬 실행 | 예 | 해당 없음 | 아니요 | 수동 |
-| Git 호환 파일 | 예 | 예 | 부분적 | 수동 |
+| Git 친화적 파일 관리 | 예 | 예 | 부분 지원 | 수동 |
| SaaS 종속성 없음 | 예 | 예 | 아니요 | 다양함 |
| 오픈 소스 (AGPL-3.0) | 예 | 예 | 아니요 | 다양함 |
+
## 라이선스
-[AGPL-3.0](LICENSE)
+[AGPL-3.0](../../LICENSE)
-## 기여
+종속성 관련 고지 사항은 [THIRD_PARTY_NOTICES.md](../../THIRD_PARTY_NOTICES.md)를 참조하세요.
-개발 설정 및 가이드라인은 [CONTRIBUTING.md](CONTRIBUTING.md)를 참조하세요. 모든 기여에는 DCO 서명이 필요합니다.
+
+## 기여
+개발 환경 설정 및 가이드라인은 [CONTRIBUTING.md](../../CONTRIBUTING.md)를 참조하세요. 모든 기여에는 DCO(Developer Certificate of Origin) 서명이 필요합니다.
diff --git a/docs/i18n/ms.md b/docs/i18n/ms.md
index 08433fd..e7dfd6e 100644
--- a/docs/i18n/ms.md
+++ b/docs/i18n/ms.md
@@ -9,29 +9,29 @@
Saluran paip pengantarabangsaan natif AI untuk projek perisian. Terjemah, sahkan, dan urus fail i18n menggunakan LLM.
[](https://github.com/Tom-R-Main/Internationalizer/actions/workflows/ci.yml)
-[](LICENSE)
+[](../../LICENSE)
-العربية · বাংলা · Deutsch · Español · Français · हिन्दी · Indonesia · 日本語 · ਪੰਜਾਬੀ · Português · Русский · తెలుగు
ไทย · Українська · 粵語 · 简体中文 · 繁體中文
+العربية · বাংলা · Čeština · Dansk · Deutsch · Ελληνικά · Español · Suomi · Français · עברית · हिन्दी · Indonesia · Italiano · 日本語 · 한국어 · Bahasa Melayu
Nederlands · ਪੰਜਾਬੀ · Polski · Português · Română · Русский · Svenska · తెలుగు · ไทย · Türkçe · Українська · Tiếng Việt · 粵語 · 简体中文 · 繁體中文
---
-
+
## Mengapa Internationalizer?
Kebanyakan alat i18n sama ada pustaka masa jalanan (i18next, react-intl) atau platform SaaS pengurusan kunci (Crowdin, Lokalise). Tiada satu pun daripadanya menyelesaikan masalah terjemahan sebenar dengan baik:
- **Terjemahan manual** tidak berskala melepasi beberapa bahasa
-- **API terjemahan mesin** (Google Translate, DeepL) mengabaikan terminologi, nada, dan resam UI anda
+- **API terjemahan mesin** (Google Translate, DeepL) mengabaikan terminologi, nada, dan konvensyen UI anda
- **Terjemahan LLM generik** berfungsi lebih baik, tetapi tanpa glosari dan panduan gaya, anda mendapat hasil yang tidak konsisten
Internationalizer adalah berbeza. Ia merupakan **saluran paip CLI** yang menggabungkan terjemahan LLM dengan:
-- **Glosari mengikut bahasa** — menguatkuasakan terminologi yang konsisten merentas aplikasi anda
-- **Panduan gaya mengikut bahasa** — mengawal nada, formaliti, pempluralan, dan tipografi
-- **Memori terjemahan** — langkau rentetan yang tidak berubah, jimatkan wang untuk panggilan API
-- **Pengesahan kunci** — tangkap terjemahan yang hilang dan ketidakpadanan interpolasi sebelum ia dilancarkan
-
+- **Glosari mengikut bahasa** — kuat kuasakan terminologi yang konsisten merentas aplikasi anda
+- **Panduan gaya mengikut bahasa** — kawal nada, formaliti, pempluralan, dan tipografi
+- **Memori terjemahan** — langkau rentetan yang tidak berubah, jimatkan kos panggilan API
+- **Pengesahan berketentuan** — tangkap kunci yang hilang atau berlebihan, anjakan struktur dilindungi, isu glosari, serta ralat jamak atau ICU sebelum ia dilancarkan
+
## Pemasangan
Pasang daripada npm:
@@ -46,7 +46,7 @@ Atau jalankan tanpa pemasangan global:
npx internationalizer --help
```
-Pakej npm memasang binari prapelbina yang sepadan daripada npm melalui kebergantungan pilihan khusus platform.
+Pakej npm memasang binari prabina yang sepadan daripada npm melalui kebergantungan pilihan khusus platform.
Pasang dengan Go:
@@ -61,15 +61,15 @@ git clone https://github.com/Tom-R-Main/Internationalizer.git
cd Internationalizer
go build -o internationalizer ./cmd/internationalizer
```
-
+
## Pakej npm
- Tag Git dan versi pakej npm mesti sepadan, contohnya `v0.1.0` dan `0.1.0`
- Pakej `internationalizer` akar bergantung pada pakej platform seperti `internationalizer-darwin-arm64`
- Sasaran npm yang disokong: macOS arm64/x64, Linux arm64/x64, Windows x64
- Penerbitan CI memerlukan rahsia GitHub bernama `NPM_TOKEN`
-
-## Mula Pantas
+
+## Permulaan Pantas
1. Cipta fail konfigurasi dalam akar projek anda:
@@ -77,7 +77,11 @@ go build -o internationalizer ./cmd/internationalizer
# .internationalizer.yml
source_locale: en
target_locales: [fr, de, es, ja]
-source_path: locales/en.json
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
llm:
provider: gemini
@@ -91,7 +95,7 @@ llm:
export GOOGLE_AI_STUDIO_API_KEY=your-ai-studio-key
```
-3. Pratonton apa yang akan diterjemahkan:
+3. Pratonton perkara yang akan diterjemahkan:
```bash
internationalizer translate --dry-run
@@ -108,44 +112,72 @@ internationalizer translate
```bash
internationalizer validate
```
-
-## Arahan
+
+## Perintah
### `translate`
-Cari kunci yang hilang dan terjemahkannya melalui LLM.
+Cari kunci yang hilang atau basi dan terjemahkannya melalui LLM.
```bash
internationalizer translate # translate all locales
internationalizer translate -l fr # translate French only
internationalizer translate --dry-run # preview without API calls
+internationalizer translate --adopt-existing # baseline existing translations without API calls
+internationalizer translate --refresh-policy # refresh prompt/style/model-stale entries
internationalizer translate --batch-size 20 # smaller batches
internationalizer translate --concurrency 2 # fewer parallel calls
```
+Keadaan terjemahan melaporkan keadaan hilang, sumber basi, dasar basi, semasa, dan diedit secara manual secara berasingan, supaya pengeditan manual tidak dapat menyembunyikan perubahan sumber atau dasar. Nilai dasar basi dilaporkan tetapi hanya diterjemahkan semula dengan `--refresh-policy`. Nilai yang diedit secara manual tidak akan ditimpa secara automatik. Gunakan `--adopt-existing` apabila memperkenalkan manifes kepada terjemahan yang telah disemak atau apabila menerima pengeditan manual yang disemak secara jelas sebagai garis dasar baharu.
+
### `validate`
-Periksa semua fail lokal untuk kunci yang hilang, kunci berlebihan, dan ketidakpadanan interpolasi.
+Periksa semua fail lokal terhadap himpunan sumbernya. Pengesahan lalai memeriksa liputan struktur (peratusan kunci sasaran yang diperlukan hadir), melaporkan kunci tambahan sebagai amaran, dan gagal bagi kunci yang hilang, ketidakpadanan interpolasi, atau struktur ICU MessageFormat yang tidak sah.
```bash
internationalizer validate # human-readable output
internationalizer validate --json # machine-readable JSON
internationalizer validate -q # exit code only
+internationalizer validate --strict # enforce translation quality rules
+internationalizer validate --require-state # require current manifest provenance
```
+`--strict` juga melaporkan liputan diterjemahkan. Nilai linguistik yang serupa dengan sumbernya dianggap belum diterjemahkan melainkan glosari secara jelas mengandungi entri sumber yang sama dan sasaran yang sama yang tepat bagi keseluruhan nilai; `ignore_case` dipatuhi, tetapi istilah glosari yang tertanam dalam nilai yang lebih panjang bukan pengecualian. Mod ketat gagal sekiranya terdapat kunci tambahan, nilai yang sama dengan sumber, perubahan struktur interpolasi/HTML/kod/pautan Markdown, pelanggaran glosari, dan bentuk jamak yang dikonfigurasikan.
+
+`--require-state` mengesahkan setiap sasaran terhadap `.internationalizer.lock`. Ia gagal apabila kunci tidak dijejaki, atau apabila sumber yang direkodkan, dasar terjemahan, atau cincangan sasaran telah basi. Pilihan ini boleh digabungkan dengan `--strict`.
+
+Laporan boleh baca manusia dan JSON menggunakan kod penemuan yang stabil:
+
+| Kod | Maksud |
+| --- | --- |
+| `missing_key` / `extra_key` | Set kunci sumber dan sasaran berbeza |
+| `blank_translation` | Sumber tidak kosong mempunyai sasaran mod ketat yang kosong |
+| `source_identical` | Nilai linguistik mod ketat kekal tidak diterjemahkan |
+| `protected_structure_mismatch` | Struktur interpolasi, HTML, kod, atau pautan telah berubah |
+| `glossary_violation` | Tiada istilah sasaran atau varian yang diluluskan dijumpai |
+| `plural_form_missing` | Bentuk jamak lokal yang dikonfigurasikan tidak wujud |
+| `icu_message_syntax` | Mesej ICU sumber atau sasaran tidak terbentuk dengan betul |
+| `icu_argument_mismatch` | Nama argumen, jenis, atau gaya pemformat ICU berbeza |
+| `icu_selector_mismatch` | Pemilih berbeza atau kategori jamak tidak sah untuk lokal sasaran |
+| `untracked` | Tiada rekod manifes wujud untuk sasaran |
+| `source_stale` | Kandungan sumber berubah selepas terjemahan direkodkan |
+| `policy_stale` | Gesaan yang dijana atau tetapan model telah berubah |
+| `target_modified` | Kandungan sasaran berbeza daripada rekod manifes |
+
### `detect`
-Kesan secara automatik rangka kerja i18n dan cadangkan konfigurasi.
+Kesan rangka kerja i18n secara automatik dan cadangkan konfigurasi.
```bash
internationalizer detect
```
-Menyokong: react-i18next, next-intl, vue-i18n, vanilla JSON, dokumen markdown.
+Menyokong: react-i18next, next-intl, vue-i18n, JSON biasa, dokumen markdown.
### `glossary`
-Urus terma glosari mengikut bahasa yang dikuatkuasakan semasa terjemahan.
+Urus istilah glosari mengikut bahasa yang dikuatkuasakan semasa terjemahan.
```bash
internationalizer glossary list --locale fr
@@ -155,14 +187,14 @@ internationalizer glossary remove --locale fr --source "Dashboard"
### `tm`
-Urus memori terjemahan (cache JSONL untuk rentetan yang telah diterjemahkan sebelum ini).
+Urus memori terjemahan (cache JSONL bagi rentetan yang telah diterjemahkan sebelum ini).
```bash
internationalizer tm stats # show record counts
internationalizer tm export # dump as JSON
internationalizer tm clear --force # delete all records
```
-
+
## Rujukan Konfigurasi
```yaml
@@ -172,10 +204,23 @@ internationalizer tm clear --force # delete all records
source_locale: en
# Languages to translate into (required)
-target_locales: [fr, de, es, ja, zh-CN, ar]
-
-# Path to the source locale file (required)
-source_path: locales/en.json
+target_locales: [fr, de, es, ja, yue, zh-CN, zh-TW, ar]
+
+# One or more source-to-target mappings (required).
+# {locale} is replaced with each configured target locale.
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
+ - id: docs
+ source: README.md
+ target: docs/i18n/{locale}.md
+ format: markdown
+
+# Backward compatibility: source_path still maps targets to sibling files
+# such as locales/fr.json. Prefer bundles for new projects.
+# source_path: locales/en.json
# LLM provider settings
llm:
@@ -184,7 +229,7 @@ llm:
# Model name defaults by provider:
# anthropic: claude-opus-5
- # openai: gpt-5.6-luna
+ # openai: gpt-5.6-luna (reasoning effort defaults to max)
# gemini: gemini-3.8-flash
# openrouter: deepseek/deepseek-v4-pro-0813
model: gemini-3.8-flash
@@ -195,6 +240,27 @@ llm:
# Base URL for OpenAI-compatible endpoints (optional)
# base_url: https://api.openai.com
+ # OpenAI GPT-5-series Responses API reasoning effort
+ # (default: max for the OpenAI provider)
+ reasoning_effort: max
+
+ # Optional LLM settings for individual target locales. An override using the
+ # global provider inherits unspecified global settings. A different provider
+ # uses that provider's defaults for unspecified settings.
+ locale_overrides:
+ yue:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-CN:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-TW:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+
# Keys per LLM call (default: 40)
batch_size: 40
@@ -209,11 +275,25 @@ glossary_dir: glossary
# Path to translation memory file (default: .internationalizer/tm.jsonl)
tm_path: .internationalizer/tm.jsonl
+
+# Versioned source, policy, target, and provenance state
+# (default: .internationalizer.lock; commit this file)
+manifest_path: .internationalizer.lock
+
+# Optional translation and strict-validation rules
+validation:
+ plural_style: i18next-v4 # generate and validate target-locale plural forms
```
+Pengecam lokal mestilah tag BCP 47 yang dibentuk dengan betul seperti `fr`, `pt-BR`, atau `sr-Latn-RS`. Lokal sasaran yang setara secara kanonik akan ditolak sebagai pendua, dan penggantian penyedia khusus lokal dipadankan mengikut ejaan setara secara kanonik. Dalam contoh di atas, lokal tanpa penggantian—termasuk bahasa Jepun—mewarisi konfigurasi Gemini global.
+
+Nilai ICU MessageFormat dihuraikan secara struktur. Argumen mudah, `select`, `plural`, `selectordinal`, `number`, `date`, dan `time` disokong, termasuk mesej bersarang, ofset jamak, pemilih nombor tepat, dan `#`. Pengesahan memeriksa sintaks, jenis argumen dan gaya pemformat, ofset jamak, identiti cabang pemilih, serta kategori jamak CLDR lokal sasaran. Output penyedia yang melanggar ketakberubahan ini akan ditolak sebelum fail lokal atau rekod memori terjemahan ditulis.
+
+Dengan `i18next-v4`, keluarga jamak sumber yang dikenal pasti dikembangkan semasa terjemahan kepada kategori CLDR lokal sasaran. Kategori khusus sasaran menggunakan nilai `_other` keluarga sumber sebagai templat terjemahannya. Pengesahan ketat memerlukan kategori sasaran tersebut; kategori khusus sumber adalah pilihan bagi lokal sasaran yang tidak menggunakannya.
+
## Panduan Gaya
-Panduan gaya ialah fail Markdown yang disuntik ke dalam gesaan terjemahan LLM. Ia mengawal nada, formaliti, tipografi, dan resam khusus bahasa yang lain.
+Panduan gaya ialah fail Markdown yang disuntik ke dalam gesaan terjemahan LLM. Panduan ini mengawal nada, formaliti, tipografi, dan konvensyen khusus bahasa yang lain.
```
style-guides/
@@ -223,16 +303,18 @@ style-guides/
ar.md # Arabic-specific rules
```
-### Resam dikongsi (`_conventions.md`)
+### Konvensyen bersama (`_conventions.md`)
-Tentukan peraturan yang terpakai untuk semua bahasa: sintaks interpolasi, pengekalan HTML, resam jenis rentetan (butang lwn. label lwn. ralat), dsb.
+Menentukan peraturan yang terpakai kepada semua bahasa: sintaks interpolasi, pemeliharaan HTML, konvensyen jenis rentetan (butang lwn. label lwn. ralat), dsb.
### Panduan mengikut bahasa (`{locale}.md`)
-Tentukan peraturan khusus bahasa: laras bahasa formaliti (tu lwn. vous), tanda baca (guillemet, tanda soal terbalik), bentuk jamak, pemformatan tarikh/nombor, dan glosari terminologi.
+Menentukan peraturan khusus bahasa: laras formaliti (tu lwn. vous), tanda baca (guillemet, tanda soal terbalik), bentuk jamak, pemformatan tarikh/nombor, dan glosari terminologi.
-Lihat [`examples/react-app/style-guides/`](examples/react-app/style-guides/) untuk contoh yang berfungsi.
+Panduan gaya ialah input dasar yang tahan lama, bukan output yang dijana. Internationalizer membaca fail ini tetapi tidak pernah menulisnya semula. Kandungannya dicincang secara berasingan daripada glosari dan kontrak gesaan, supaya perubahan kod aplikasi tidak menjadikan terjemahan basi. Mengedit panduan sengaja menandakan lokal tersebut untuk semakan dasar; menukar susunan kata gesaan dalaman tidak berbuat demikian, melainkan versi kontrak gesaan turut berubah.
+Lihat [`examples/react-app/style-guides/`](../../examples/react-app/style-guides/) untuk contoh yang berfungsi.
+
## Format Glosari
Fail glosari ialah tatasusunan JSON yang disimpan dalam `{glossary_dir}/{locale}.json`:
@@ -242,41 +324,45 @@ Fail glosari ialah tatasusunan JSON yang disimpan dalam `{glossary_dir}/{locale}
{
"source": "Dashboard",
"target": "Tableau de bord",
+ "variants": ["Panneau de contrôle"],
+ "enforcement": "error",
"ignore_case": false,
"whole_word": true
}
]
```
-Terma disuntik ke dalam gesaan LLM sebagai jadual terminologi, memastikan terjemahan terma utama yang konsisten merentas aplikasi anda.
-
+`variants` menyenaraikan bentuk sasaran lain yang diluluskan. `enforcement` boleh menjadi `error`, `warning`, atau diabaikan untuk gelagat ralat lalai. Istilah disuntik ke dalam gesaan LLM sebagai jadual istilah, memastikan terjemahan yang konsisten merentas aplikasi anda. Entri tepat seperti `{"source":"API","target":"API"}` juga mengecualikan keseluruhan nilai sumber yang sama daripada penemuan nilai belum diterjemahkan mod ketat; ini tidak mengecualikan nilai yang lebih panjang yang hanya mengandungi `API`.
+
## Memori Terjemahan
Memori terjemahan disimpan sebagai fail JSONL (satu rekod JSON setiap baris). Setiap rekod mengandungi:
-- Kunci dan nilai sumber
-- Nilai yang diterjemahkan
-- Cincangan SHA-256 bagi nilai sumber
+- Himpunan, kunci, nilai sumber, nilai diterjemahkan, dan lokal sasaran kanonik
+- Cincangan sumber, panduan gaya, glosari, kontrak gesaan, dan dasar gabungan
+- Penyedia dan model yang menghasilkan terjemahan
- Cap masa
-Pada larian seterusnya, rentetan yang tidak berubah disajikan daripada cache TM tanpa memanggil LLM, menjimatkan masa dan kos API. Fail TM mesra git dan boleh dikomit bersama fail lokal anda.
-
-## Format Disokong
+Pada larian berikutnya, rentetan dengan cincangan sumber dan dasar yang sama disajikan daripada cache tanpa memanggil LLM. Laluan lalai berada di bawah direktori `.internationalizer/` yang diabaikan, menjadikannya cache setempat. Tetapkan `tm_path` kepada lokasi yang dijejaki sekiranya projek anda sengaja berkongsi memori terjemahan. Manifes `.internationalizer.lock` yang boleh disemak diindividukan versinya secara berasingan.
+
+## Format yang Disokong
| Format | Sambungan | Mod |
|--------|-----------|------|
-| JSON | `.json` | Nilai kunci (bersarang, diratakan dengan tatatanda titik) |
-| YAML | `.yml`, `.yaml` | Nilai kunci (mengekalkan ulasan dan susunan) |
-| Markdown | `.md`, `.mdx` | Terjemahan keseluruhan dokumen |
+| JSON | `.json` | Nilai kunci (bersarang, diratakan tatatanda titik) |
+| YAML | `.yml`, `.yaml` | Nilai kunci (mengekalkan komen dan susunan) |
+| Markdown | `.md`, `.mdx` | Mukadimah dan bahagian peringkat H2 |
+Sasaran Markdown mengandungi komen `internationalizer:unit` halimunan sebelum bahagian H2. Penanda stabil ini membolehkan Internationalizer menambah, memindahkan, atau mengedit satu bahagian sumber tanpa menterjemah semula bahagian lain yang tidak berkaitan. Dokumen sedia ada yang belum ditandai akan menerima penanda pada kemas kini berjaya yang seterusnya.
+
## Pengesanan Jenis Projek
`internationalizer detect` mengenal pasti persediaan i18n anda dengan memeriksa:
- Kebergantungan `package.json` untuk react-i18next, next-intl, atau vue-i18n
-- Struktur direktori yang sepadan dengan corak lokal biasa
-- Sambungan fail dan resam penamaan
-
+- Struktur direktori yang sepadan dengan corak lokal lazim
+- Sambungan fail dan konvensyen penamaan
+
## Seni Bina
```
@@ -291,12 +377,16 @@ internal/
openai.go OpenAI / compatible backend
gemini.go Google Gemini via AI Studio backend
OpenRouter uses openai.go with custom base_url
+ locale/ BCP 47 identity and CLDR plural categories
+ message/ ICU MessageFormat parser and structural comparison
+ policy/ Stable translation-policy hashing
+ state/ Versioned translation manifest
styleguide/ Style guide loader
tm/ JSONL translation memory
translate/ Translation orchestrator
validate/ Locale validation and diffing
```
-
+
## Perbandingan dengan Alternatif
| Ciri | Internationalizer | i18next | Crowdin | LLM Generik |
@@ -305,16 +395,17 @@ internal/
| Panduan gaya mengikut bahasa | Ya | Tidak | Tidak | Tidak |
| Penguatkuasaan glosari | Ya | Tidak | Ya | Tidak |
| Memori terjemahan | Ya | Tidak | Ya | Tidak |
-| Pelaksanaan CLI / tempatan | Ya | N/A | Tidak | Manual |
+| Pelaksanaan CLI / tempatan | Ya | T/B | Tidak | Manual |
| Fail mesra Git | Ya | Ya | Separa | Manual |
| Tiada kebergantungan SaaS | Ya | Ya | Tidak | Berbeza-beza |
| Sumber terbuka (AGPL-3.0) | Ya | Ya | Tidak | Berbeza-beza |
-
+
## Lesen
-[AGPL-3.0](LICENSE)
+[AGPL-3.0](../../LICENSE)
+Lihat [THIRD_PARTY_NOTICES.md](../../THIRD_PARTY_NOTICES.md) untuk notis kebergantungan.
+
## Menyumbang
-Lihat [CONTRIBUTING.md](CONTRIBUTING.md) untuk persediaan pembangunan dan garis panduan. Semua sumbangan memerlukan persetujuan DCO.
-
+Lihat [CONTRIBUTING.md](../../CONTRIBUTING.md) untuk persediaan pembangunan dan garis panduan. Semua sumbangan memerlukan perakuan DCO.
diff --git a/docs/i18n/nl.md b/docs/i18n/nl.md
index d65eee9..129e4ba 100644
--- a/docs/i18n/nl.md
+++ b/docs/i18n/nl.md
@@ -6,32 +6,34 @@
# Internationalizer
-AI-native internationalisatie-pipeline voor softwareprojecten. Vertaal, valideer en beheer i18n-bestanden met behulp van LLM's.
+AI-native internationalisatiepipeline voor softwareprojecten. Vertaal, valideer en beheer i18n-bestanden met behulp van LLM's.
[](https://github.com/Tom-R-Main/Internationalizer/actions/workflows/ci.yml)
-[](LICENSE)
+[](../../LICENSE)
-العربية · বাংলা · Deutsch · Español · Français · हिन्दी · Indonesia · 日本語 · ਪੰਜਾਬੀ · Português · Русский · తెలుగు
ไทย · Українська · 粵語 · 简体中文 · 繁體中文
+العربية · বাংলা · Čeština · Dansk · Deutsch · Ελληνικά · Español · Suomi · Français · עברית · हिन्दी · Indonesia · Italiano · 日本語 · 한국어 · Bahasa Melayu
Nederlands · ਪੰਜਾਬੀ · Polski · Português · Română · Русский · Svenska · తెలుగు · ไทย · Türkçe · Українська · Tiếng Việt · 粵語 · 简体中文 · 繁體中文
---
+
## Waarom Internationalizer?
-De meeste i18n-tools zijn ofwel runtime-bibliotheken (i18next, react-intl) of SaaS-platforms voor sleutelbeheer (Crowdin, Lokalise). Geen van deze lost het daadwerkelijke vertaalprobleem goed op:
+De meeste i18n-tools zijn ofwel runtimebibliotheken (i18next, react-intl) of SaaS-platforms voor sleutelbeheer (Crowdin, Lokalise). Geen van beide lost het daadwerkelijke vertaalprobleem goed op:
-- **Handmatige vertaling** is niet schaalbaar voorbij een paar talen
+- **Handmatige vertaling** schaalt niet voorbij een paar talen
- **Machinevertaling-API's** (Google Translate, DeepL) negeren je terminologie, toon en UI-conventies
- **Generieke LLM-vertaling** werkt beter, maar zonder woordenlijsten en stijlgidsen krijg je inconsistente resultaten
-Internationalizer is anders. Het is een **CLI-pipeline** die LLM-vertaling combineert met:
+Internationalizer pakt het anders aan. Het is een **CLI-pipeline** die LLM-vertaling combineert met:
- **Woordenlijsten per taal** — dwing consistente terminologie af in je hele app
-- **Stijlgidsen per taal** — beheer toon, formaliteit, meervoudsvormen en typografie
-- **Vertaalgeheugen** — sla ongewijzigde strings over en bespaar geld op API-aanroepen
-- **Sleutelvalidatie** — spoor ontbrekende vertalingen en interpolatiefouten op voordat ze live gaan
+- **Stijlgidsen per taal** — stuur toon, formaliteit, meervoudsvormen en typografie aan
+- **Vertaalgeheugen** — sla ongewijzigde strings over en bespaar kosten op API-aanroepen
+- **Deterministische validatie** — spoor ontbrekende of overtollige sleutels, afwijkingen in beschermde structuren, terminologieproblemen en meervouds- of ICU-fouten op voordat ze in productie gaan
+
## Installatie
Installeer via npm:
@@ -46,7 +48,7 @@ Of voer uit zonder globale installatie:
npx internationalizer --help
```
-Het npm-pakket installeert de bijbehorende voorgebouwde binary via platformspecifieke optionele afhankelijkheden.
+Het npm-pakket installeert de bijbehorende voorgebouwde binary vanaf npm via platformspecifieke optionele afhankelijkheden.
Installeer met Go:
@@ -62,13 +64,15 @@ cd Internationalizer
go build -o internationalizer ./cmd/internationalizer
```
+
## npm-pakketten
-- Git-tags en npm-pakketversies moeten overeenkomen, bijvoorbeeld `v0.1.0` en `0.1.0`
-- Het hoofd-`internationalizer`-pakket is afhankelijk van platformpakketten zoals `internationalizer-darwin-arm64`
-- Ondersteunde npm-targets: macOS arm64/x64, Linux arm64/x64, Windows x64
-- CI-publicatie vereist een GitHub-secret genaamd `NPM_TOKEN`
+- Git-tags en npm-pakketversies moeten exact overeenkomen, bijvoorbeeld `v0.1.0` en `0.1.0`
+- Het rootpakket `internationalizer` is afhankelijk van platformspecifieke pakketten zoals `internationalizer-darwin-arm64`
+- Ondersteunde npm-platforms: macOS arm64/x64, Linux arm64/x64, Windows x64
+- Publicatie via CI vereist een GitHub-secret met de naam `NPM_TOKEN`
+
## Snel aan de slag
1. Maak een configuratiebestand aan in de hoofdmap van je project:
@@ -77,7 +81,11 @@ go build -o internationalizer ./cmd/internationalizer
# .internationalizer.yml
source_locale: en
target_locales: [fr, de, es, ja]
-source_path: locales/en.json
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
llm:
provider: gemini
@@ -91,7 +99,7 @@ llm:
export GOOGLE_AI_STUDIO_API_KEY=your-ai-studio-key
```
-3. Bekijk een voorbeeld van wat er vertaald gaat worden:
+3. Bekijk vooraf wat er vertaald gaat worden:
```bash
internationalizer translate --dry-run
@@ -103,36 +111,65 @@ internationalizer translate --dry-run
internationalizer translate
```
-5. Valideer alle talen:
+5. Valideer alle doeltalen:
```bash
internationalizer validate
```
+
## Commando's
### `translate`
-Vind ontbrekende sleutels en vertaal ze via een LLM.
+Zoek ontbrekende of verouderde sleutels en vertaal ze via een LLM.
```bash
internationalizer translate # translate all locales
internationalizer translate -l fr # translate French only
internationalizer translate --dry-run # preview without API calls
+internationalizer translate --adopt-existing # baseline existing translations without API calls
+internationalizer translate --refresh-policy # refresh prompt/style/model-stale entries
internationalizer translate --batch-size 20 # smaller batches
internationalizer translate --concurrency 2 # fewer parallel calls
```
+De vertaalstatus rapporteert onafhankelijk over ontbrekende, brongewijzigde (source-stale), beleidsgewijzigde (policy-stale), actuele en handmatig bewerkte situaties. Hierdoor kan een handmatige aanpassing nooit een bron- of beleidswijziging maskeren. Beleidsgewijzigde waarden worden wel gemeld, maar uitsluitend opnieuw vertaald met `--refresh-policy`. Handmatig bewerkte waarden worden nooit automatisch overschreven. Gebruik `--adopt-existing` wanneer je het manifest introduceert voor reeds gecontroleerde vertalingen of wanneer je een gecontroleerde handmatige bewerking expliciet als nieuwe uitgangssituatie accepteert.
+
### `validate`
-Controleer alle taalbestanden op ontbrekende sleutels, extra sleutels en interpolatiefouten.
+Controleer alle doelbestanden tegen hun bronbundels. De standaardvalidatie verifieert de structurele dekking (het percentage aanwezige vereiste doelsleutels), meldt overtollige sleutels als waarschuwingen en mislukt bij ontbrekende sleutels, niet-overeenkomende interpolatievariabelen of een ongeldige ICU MessageFormat-structuur.
```bash
internationalizer validate # human-readable output
internationalizer validate --json # machine-readable JSON
internationalizer validate -q # exit code only
+internationalizer validate --strict # enforce translation quality rules
+internationalizer validate --require-state # require current manifest provenance
```
+`--strict` rapporteert daarnaast de vertaalde dekking. Een taalkundige waarde die identiek is aan de bron geldt als onvertaald, tenzij de woordenlijst expliciet een exacte vermelding bevat met dezelfde bron en hetzelfde doel voor de volledige waarde; hierbij wordt rekening gehouden met `ignore_case`, maar een woordenlijstterm die deel uitmaakt van een langere string geldt niet als uitzondering. De strikte modus faalt op overtollige sleutels, met de bron identieke waarden, gewijzigde interpolatie-, HTML-, code- of Markdown-linkstructuren, woordenlijstovertredingen en geconfigureerde meervoudsvormen.
+
+`--require-state` verifieert elk doelbestand tegen `.internationalizer.lock`. Dit commando faalt wanneer een sleutel niet wordt bijgehouden, of wanneer de vastgelegde bron, het vertaalbeleid of de doelhash verouderd is. Dit kan worden gecombineerd met `--strict`.
+
+De rapportages voor mensen en JSON gebruiken vaste bevindingcodes:
+
+| Code | Betekenis |
+| --- | --- |
+| `missing_key` / `extra_key` | De sleutelsets van bron en doel wijken af |
+| `blank_translation` | Een niet-lege bronwaarde heeft een leeg doel in strikte modus |
+| `source_identical` | Een taalkundige waarde in strikte modus is onvertaald gebleven |
+| `protected_structure_mismatch` | De opzet van interpolaties, HTML, code of links is gewijzigd |
+| `glossary_violation` | Er is geen goedgekeurde doelterm of goedgekeurde variant gevonden |
+| `plural_form_missing` | Een geconfigureerde meervoudsvorm voor de doeltaal ontbreekt |
+| `icu_message_syntax` | Een ICU-bericht in de bron of het doel bevat syntaxfouten |
+| `icu_argument_mismatch` | ICU-argumentnamen, typen of formatteerstijlen komen niet overeen |
+| `icu_selector_mismatch` | Selectors wijken af of een meervoudscategorie is ongeldig voor de doeltaal |
+| `untracked` | Er bestaat geen manifestvermelding voor het doel |
+| `source_stale` | De broninhoud is gewijzigd na de vastgelegde vertaling |
+| `policy_stale` | De gegenereerde prompt of modelinstellingen zijn gewijzigd |
+| `target_modified` | De inhoud van het doel wijkt af van de vermelding in het manifest |
+
### `detect`
Detecteer automatisch het i18n-framework en stel een configuratie voor.
@@ -141,11 +178,11 @@ Detecteer automatisch het i18n-framework en stel een configuratie voor.
internationalizer detect
```
-Ondersteunt: react-i18next, next-intl, vue-i18n, vanilla JSON, markdown-documenten.
+Ondersteunt: react-i18next, next-intl, vue-i18n, standaard JSON, Markdown-documentatie.
### `glossary`
-Beheer woordenlijsttermen per taal die worden afgedwongen tijdens de vertaling.
+Beheer woordenlijsttermen per taal die tijdens de vertaling worden afgedwongen.
```bash
internationalizer glossary list --locale fr
@@ -163,6 +200,7 @@ internationalizer tm export # dump as JSON
internationalizer tm clear --force # delete all records
```
+
## Configuratiereferentie
```yaml
@@ -172,10 +210,23 @@ internationalizer tm clear --force # delete all records
source_locale: en
# Languages to translate into (required)
-target_locales: [fr, de, es, ja, zh-CN, ar]
-
-# Path to the source locale file (required)
-source_path: locales/en.json
+target_locales: [fr, de, es, ja, yue, zh-CN, zh-TW, ar]
+
+# One or more source-to-target mappings (required).
+# {locale} is replaced with each configured target locale.
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
+ - id: docs
+ source: README.md
+ target: docs/i18n/{locale}.md
+ format: markdown
+
+# Backward compatibility: source_path still maps targets to sibling files
+# such as locales/fr.json. Prefer bundles for new projects.
+# source_path: locales/en.json
# LLM provider settings
llm:
@@ -184,7 +235,7 @@ llm:
# Model name defaults by provider:
# anthropic: claude-opus-5
- # openai: gpt-5.6-luna
+ # openai: gpt-5.6-luna (reasoning effort defaults to max)
# gemini: gemini-3.8-flash
# openrouter: deepseek/deepseek-v4-pro-0813
model: gemini-3.8-flash
@@ -195,6 +246,27 @@ llm:
# Base URL for OpenAI-compatible endpoints (optional)
# base_url: https://api.openai.com
+ # OpenAI GPT-5-series Responses API reasoning effort
+ # (default: max for the OpenAI provider)
+ reasoning_effort: max
+
+ # Optional LLM settings for individual target locales. An override using the
+ # global provider inherits unspecified global settings. A different provider
+ # uses that provider's defaults for unspecified settings.
+ locale_overrides:
+ yue:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-CN:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-TW:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+
# Keys per LLM call (default: 40)
batch_size: 40
@@ -209,11 +281,26 @@ glossary_dir: glossary
# Path to translation memory file (default: .internationalizer/tm.jsonl)
tm_path: .internationalizer/tm.jsonl
+
+# Versioned source, policy, target, and provenance state
+# (default: .internationalizer.lock; commit this file)
+manifest_path: .internationalizer.lock
+
+# Optional translation and strict-validation rules
+validation:
+ plural_style: i18next-v4 # generate and validate target-locale plural forms
```
+Taal-ID's moeten correct opgemaakte BCP 47-tags zijn zoals `fr`, `pt-BR` of `sr-Latn-RS`. Canoniek gelijkwaardige doeltalen worden geweigerd als duplicaten, en taalspecifieke provider-overrides matchen op canoniek gelijkwaardige schrijfwijzen. In het bovenstaande voorbeeld nemen doeltalen zonder override — inclusief Japans — de globale Gemini-configuratie over.
+
+ICU MessageFormat-waarden worden structureel geanalyseerd. Eenvoudige argumenten, `select`, `plural`, `selectordinal`, `number`, `date` en `time` worden ondersteund, inclusief geneste berichten, meervoudsoffsets, selectors met exacte getallen en `#`. Validatie controleert syntax, argumenttypen, formatteerstijlen, meervoudsoffsets, de identiteit van select-takken en de CLDR-meervoudscategorieën van de doeltaal. Uitvoer van providers die deze invarianten schendt, wordt afgewezen voordat een doeltaalbestand of vertaalgeheugenrecord wordt weggeschreven.
+
+Met `i18next-v4` worden herkende meervoudsfamilies in de bron tijdens de vertaling uitgebreid naar de CLDR-categorieën van de doeltaal. Een categorie die alleen in de doeltaal voorkomt, gebruikt de `_other`-waarde van de bronfamilie als vertaalsjabloon. Strikte validatie vereist deze doelcategorieën; categorieën die alleen in de bron voorkomen, zijn optioneel voor doeltalen die deze niet hanteren.
+
+
## Stijlgidsen
-Stijlgidsen zijn Markdown-bestanden die worden geïnjecteerd in de LLM-vertaalprompt. Ze beheren de toon, formaliteit, typografie en andere taalspecifieke conventies.
+Stijlgidsen zijn Markdown-bestanden die worden toegevoegd aan de LLM-vertaalprompt. Ze sturen de toon, formaliteit, typografie en andere taalspecifieke conventies aan.
```
style-guides/
@@ -225,15 +312,18 @@ style-guides/
### Gedeelde conventies (`_conventions.md`)
-Definieer regels die van toepassing zijn op alle talen: interpolatiesyntaxis, behoud van HTML, conventies voor stringtypen (knoppen vs. labels vs. foutmeldingen), enz.
+Definieer regels die gelden voor alle talen: interpolatiesyntaxis, behoud van HTML, conventies voor stringtypen (knoppen vs. labels vs. foutmeldingen), enzovoort.
### Gidsen per taal (`{locale}.md`)
-Definieer taalspecifieke regels: formaliteitsregister (tu vs. vous), interpunctie (guillemets, omgekeerde vraagtekens), meervoudsvormen, datum-/getalnotatie en een terminologiewoordenlijst.
+Definieer taalspecifieke regels: formaliteitsregister (je/jouw vs. u/uw), interpunctie (aanhalingstekens, gedachtestreepjes), meervoudsvormen, opmaak van datums en getallen, en een terminologiewoordenlijst.
-Zie [`examples/react-app/style-guides/`](examples/react-app/style-guides/) voor een werkend voorbeeld.
+Stijlgidsen zijn duurzame beleidsinvoer, geen gegenereerde uitvoer. Internationalizer leest ze uit, maar herschrijft ze nooit. De inhoud ervan wordt afzonderlijk gehasht van de woordenlijst en het promptcontract, waardoor een codewijziging in de applicatie een vertaling niet zomaar als verouderd markeert. Het bewerken van een gids markeert die taal bewust voor beleidsbeoordeling; interne aanpassingen aan promptformuleringen doen dat niet, tenzij ook de versie van het promptcontract wijzigt.
-## Woordenlijstformaat
+Zie [`examples/react-app/style-guides/`](../../examples/react-app/style-guides/) voor een werkend voorbeeld.
+
+
+## Woordenlijstindeling
Woordenlijstbestanden zijn JSON-arrays die worden opgeslagen in `{glossary_dir}/{locale}.json`:
@@ -242,41 +332,49 @@ Woordenlijstbestanden zijn JSON-arrays die worden opgeslagen in `{glossary_dir}/
{
"source": "Dashboard",
"target": "Tableau de bord",
+ "variants": ["Panneau de contrôle"],
+ "enforcement": "error",
"ignore_case": false,
"whole_word": true
}
]
```
-Termen worden in de LLM-prompt geïnjecteerd als een terminologietabel, wat zorgt voor een consistente vertaling van belangrijke termen in je hele applicatie.
+`variants` geeft andere goedgekeurde doelvarianten weer. `enforcement` kan worden ingesteld op `error`, `warning`, of worden weggelaten voor het standaardfoutgedrag. Termen worden als terminologietabel aan de LLM-prompt toegevoegd, wat zorgt voor een consistente vertaling binnen je hele applicatie. Een exacte vermelding zoals `{"source":"API","target":"API"}` stelt die volledige, met de bron identieke waarde bovendien vrij van meldingen over onvertaalde waarden in de strikte modus; een langere waarde die toevallig `API` bevat, wordt hiermee niet vrijgesteld.
+
## Vertaalgeheugen
Het vertaalgeheugen wordt opgeslagen als een JSONL-bestand (één JSON-record per regel). Elk record bevat:
-- De bronsleutel en -waarde
-- De vertaalde waarde
-- Een SHA-256-hash van de bronwaarde
+- De bundel, sleutel, bronwaarde, vertaalde waarde en canonieke doeltaal
+- Hashes van de bron, de stijlgids, de woordenlijst, het promptcontract en het gecombineerde beleid
+- De provider en het model waarmee de vertaling is gegenereerd
- Een tijdstempel
-Bij volgende uitvoeringen worden ongewijzigde strings uit de TM-cache gehaald zonder de LLM aan te roepen, wat zowel tijd als API-kosten bespaart. Het TM-bestand is git-vriendelijk en kan samen met je taalbestanden worden gecommit.
+Bij volgende runs worden strings met dezelfde bron- en beleidshashes direct vanuit de cache geleverd zonder de LLM aan te roepen. Het standaardpad bevindt zich in de genegeerde map `.internationalizer/`, waardoor het een lokale cache blijft. Stel `tm_path` in op een getrackte locatie als je project het vertaalgeheugen bewust wil delen via versiebeheer. Het controleerbare `.internationalizer.lock`-manifest wordt apart geversioneerd.
-## Ondersteunde formaten
+
+## Ondersteunde bestandsindelingen
| Formaat | Extensies | Modus |
-|--------|-----------|------|
-| JSON | `.json` | Key-value (genest, afgevlakt met puntnotatie) |
-| YAML | `.yml`, `.yaml` | Key-value (behoudt opmerkingen en volgorde) |
-| Markdown | `.md`, `.mdx` | Vertaling van het hele document |
+|---|---|---|
+| JSON | `.json` | Sleutel-waarde (genest, afgevlakt met puntnotatie) |
+| YAML | `.yml`, `.yaml` | Sleutel-waarde (behoudt commentaren en volgorde) |
+| Markdown | `.md`, `.mdx` | Preambule en secties op H2-niveau |
+
+Markdown-doelbestanden bevatten onzichtbare `internationalizer:unit`-commentaren vóór H2-secties. Dankzij deze stabiele markeringen kan Internationalizer één bronsectie toevoegen, verplaatsen of bewerken zonder niet-gerelateerde secties opnieuw te vertalen. Bestaande niet-gemarkeerde documenten krijgen deze markeringen bij de eerstvolgende succesvolle bijwerking.
-## Detectie van projecttype
+
+## Projecttypedetectie
-`internationalizer detect` identificeert je i18n-setup door te controleren op:
+`internationalizer detect` herkent je i18n-configuratie door te controleren op:
-- `package.json`-afhankelijkheden voor react-i18next, next-intl of vue-i18n
-- Mappenstructuren die overeenkomen met veelvoorkomende taalpatronen
+- Afhankelijkheden in `package.json` voor react-i18next, next-intl of vue-i18n
+- Mappenstructuren die overeenkomen met gangbare taalpatronen
- Bestandsextensies en naamgevingsconventies
+
## Architectuur
```
@@ -291,30 +389,38 @@ internal/
openai.go OpenAI / compatible backend
gemini.go Google Gemini via AI Studio backend
OpenRouter uses openai.go with custom base_url
+ locale/ BCP 47 identity and CLDR plural categories
+ message/ ICU MessageFormat parser and structural comparison
+ policy/ Stable translation-policy hashing
+ state/ Versioned translation manifest
styleguide/ Style guide loader
tm/ JSONL translation memory
translate/ Translation orchestrator
validate/ Locale validation and diffing
```
+
## Vergelijking met alternatieven
| Functie | Internationalizer | i18next | Crowdin | Generieke LLM |
-|---------|------------------|---------|---------|-------------|
-| LLM-aangedreven vertaling | Ja | Nee | Gedeeltelijk | Ja |
+|---|---|---|---|---|
+| LLM-gestuurde vertaling | Ja | Nee | Gedeeltelijk | Ja |
| Stijlgidsen per taal | Ja | Nee | Nee | Nee |
-| Handhaving van woordenlijst | Ja | Nee | Ja | Nee |
+| Woordenlijsthandhaving | Ja | Nee | Ja | Nee |
| Vertaalgeheugen | Ja | Nee | Ja | Nee |
| CLI / lokale uitvoering | Ja | N.v.t. | Nee | Handmatig |
| Git-vriendelijke bestanden | Ja | Ja | Gedeeltelijk | Handmatig |
| Geen SaaS-afhankelijkheid | Ja | Ja | Nee | Varieert |
| Open source (AGPL-3.0) | Ja | Ja | Nee | Varieert |
+
## Licentie
-[AGPL-3.0](LICENSE)
+[AGPL-3.0](../../LICENSE)
-## Bijdragen
+Zie [THIRD_PARTY_NOTICES.md](../../THIRD_PARTY_NOTICES.md) voor vermeldingen van afhankelijkheden.
-Zie [CONTRIBUTING.md](CONTRIBUTING.md) voor de ontwikkelingssetup en richtlijnen. Alle bijdragen vereisen een DCO-aftekening.
+
+## Bijdragen
+Zie [CONTRIBUTING.md](../../CONTRIBUTING.md) voor instructies over het opzetten van de ontwikkelomgeving en richtlijnen. Alle bijdragen vereisen een DCO-ondertekening.
diff --git a/docs/i18n/pa.md b/docs/i18n/pa.md
index 38cb060..823f13a 100644
--- a/docs/i18n/pa.md
+++ b/docs/i18n/pa.md
@@ -9,8 +9,15 @@
ਸਾਫਟਵੇਅਰ ਪ੍ਰੋਜੈਕਟਾਂ ਲਈ AI-native internationalization ਪਾਈਪਲਾਈਨ। LLMs ਦੀ ਵਰਤੋਂ ਕਰਕੇ i18n ਫਾਈਲਾਂ ਦਾ ਅਨੁਵਾਦ ਕਰੋ, ਪ੍ਰਮਾਣਿਤ ਕਰੋ ਅਤੇ ਪ੍ਰਬੰਧਿਤ ਕਰੋ।
[](https://github.com/Tom-R-Main/Internationalizer/actions/workflows/ci.yml)
-[](LICENSE)
+[](../../LICENSE)
+
+العربية · বাংলা · Čeština · Dansk · Deutsch · Ελληνικά · Español · Suomi · Français · עברית · हिन्दी · Indonesia · Italiano · 日本語 · 한국어 · Bahasa Melayu
Nederlands · ਪੰਜਾਬੀ · Polski · Português · Română · Русский · Svenska · తెలుగు · ไทย · Türkçe · Українська · Tiếng Việt · 粵語 · 简体中文 · 繁體中文
+
+
+---
+
+
## Internationalizer ਕਿਉਂ?
ਜ਼ਿਆਦਾਤਰ i18n ਟੂਲ ਜਾਂ ਤਾਂ ਰਨਟਾਈਮ ਲਾਇਬ੍ਰੇਰੀਆਂ (i18next, react-intl) ਹਨ ਜਾਂ key-management SaaS ਪਲੇਟਫਾਰਮ (Crowdin, Lokalise) ਹਨ। ਇਹਨਾਂ ਵਿੱਚੋਂ ਕੋਈ ਵੀ ਅਸਲ ਅਨੁਵਾਦ ਸਮੱਸਿਆ ਨੂੰ ਚੰਗੀ ਤਰ੍ਹਾਂ ਹੱਲ ਨਹੀਂ ਕਰਦਾ:
@@ -22,10 +29,11 @@
Internationalizer ਵੱਖਰਾ ਹੈ। ਇਹ ਇੱਕ **CLI ਪਾਈਪਲਾਈਨ** ਹੈ ਜੋ LLM ਅਨੁਵਾਦ ਨੂੰ ਇਹਨਾਂ ਨਾਲ ਜੋੜਦੀ ਹੈ:
- **ਪ੍ਰਤੀ-ਭਾਸ਼ਾ ਗਲੋਸਰੀਆਂ** — ਤੁਹਾਡੀ ਐਪ ਵਿੱਚ ਇਕਸਾਰ ਸ਼ਬਦਾਵਲੀ ਲਾਗੂ ਕਰੋ
-- **ਪ੍ਰਤੀ-ਭਾਸ਼ਾ ਸਟਾਈਲ ਗਾਈਡਾਂ** — ਟੋਨ, ਰਸਮੀਪਣ, ਬਹੁਵਚਨ ਅਤੇ ਟਾਈਪੋਗ੍ਰਾਫੀ ਨੂੰ ਕੰਟਰੋਲ ਕਰੋ
+- **ਪ੍ਰਤੀ-ਭਾਸ਼ਾ ਸਟਾਈਲ ਗਾਈਡਾਂ** — ਟੋਨ, ਰਸਮੀਪਣ, ਬਹੁਵਚਨ ਰੂਪਾਂ ਅਤੇ ਟਾਈਪੋਗ੍ਰਾਫੀ ਨੂੰ ਕੰਟਰੋਲ ਕਰੋ
- **ਅਨੁਵਾਦ ਮੈਮੋਰੀ** — ਨਾ ਬਦਲੀਆਂ ਸਟ੍ਰਿੰਗਾਂ ਨੂੰ ਛੱਡੋ, API ਕਾਲਾਂ 'ਤੇ ਪੈਸੇ ਬਚਾਓ
-- **ਕੁੰਜੀ ਪ੍ਰਮਾਣਿਕਤਾ** — ਸ਼ਿਪ ਕਰਨ ਤੋਂ ਪਹਿਲਾਂ ਗੁੰਮ ਹੋਏ ਅਨੁਵਾਦਾਂ ਅਤੇ ਇੰਟਰਪੋਲੇਸ਼ਨ ਬੇਮੇਲਾਂ ਨੂੰ ਫੜੋ
+- **ਨਿਰਧਾਰਿਤ ਪ੍ਰਮਾਣੀਕਰਨ** — ਸ਼ਿਪ ਕਰਨ ਤੋਂ ਪਹਿਲਾਂ ਗੁੰਮ ਜਾਂ ਵਾਧੂ ਕੁੰਜੀਆਂ, ਸੁਰੱਖਿਅਤ ਬਣਤਰ ਬਦਲਾਵਾਂ, ਗਲੋਸਰੀ ਸੰਬੰਧੀ ਮੁੱਦਿਆਂ ਅਤੇ ਬਹੁਵਚਨ ਜਾਂ ICU ਗਲਤੀਆਂ ਨੂੰ ਫੜੋ
+
## ਇੰਸਟਾਲੇਸ਼ਨ
npm ਤੋਂ ਇੰਸਟਾਲ ਕਰੋ:
@@ -40,7 +48,7 @@ npm install -g internationalizer
npx internationalizer --help
```
-npm ਪੈਕੇਜ ਪਲੇਟਫਾਰਮ-ਵਿਸ਼ੇਸ਼ ਵਿਕਲਪਿਕ ਨਿਰਭਰਤਾਵਾਂ ਰਾਹੀਂ npm ਤੋਂ ਮੇਲ ਖਾਂਦੀ ਪ੍ਰੀਬਿਲਟ ਬਾਈਨਰੀ ਨੂੰ ਇੰਸਟਾਲ ਕਰਦਾ ਹੈ।
+npm ਪੈਕੇਜ ਪਲੇਟਫਾਰਮ-ਵਿਸ਼ੇਸ਼ ਵਿਕਲਪਿਕ ਨਿਰਭਰਤਾਵਾਂ ਰਾਹੀਂ npm ਤੋਂ ਮੇਲ ਖਾਂਦੀ ਪਹਿਲਾਂ ਤੋਂ ਬਣੀ ਬਾਈਨਰੀ ਨੂੰ ਇੰਸਟਾਲ ਕਰਦਾ ਹੈ।
Go ਨਾਲ ਇੰਸਟਾਲ ਕਰੋ:
@@ -56,6 +64,7 @@ cd Internationalizer
go build -o internationalizer ./cmd/internationalizer
```
+
## npm ਪੈਕੇਜ
- Git ਟੈਗ ਅਤੇ npm ਪੈਕੇਜ ਵਰਜਨ ਮੇਲ ਖਾਂਦੇ ਹੋਣੇ ਚਾਹੀਦੇ ਹਨ, ਉਦਾਹਰਨ ਲਈ `v0.1.0` ਅਤੇ `0.1.0`
@@ -63,6 +72,7 @@ go build -o internationalizer ./cmd/internationalizer
- ਸਮਰਥਿਤ npm ਟੀਚੇ: macOS arm64/x64, Linux arm64/x64, Windows x64
- CI ਪਬਲਿਸ਼ਿੰਗ ਲਈ `NPM_TOKEN` ਨਾਮਕ GitHub ਸੀਕ੍ਰੇਟ ਦੀ ਲੋੜ ਹੁੰਦੀ ਹੈ
+
## ਤੁਰੰਤ ਸ਼ੁਰੂਆਤ
1. ਆਪਣੇ ਪ੍ਰੋਜੈਕਟ ਰੂਟ ਵਿੱਚ ਇੱਕ ਕੌਂਫਿਗ ਫਾਈਲ ਬਣਾਓ:
@@ -71,7 +81,11 @@ go build -o internationalizer ./cmd/internationalizer
# .internationalizer.yml
source_locale: en
target_locales: [fr, de, es, ja]
-source_path: locales/en.json
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
llm:
provider: gemini
@@ -103,30 +117,59 @@ internationalizer translate
internationalizer validate
```
+
## ਕਮਾਂਡਾਂ
### `translate`
-ਗੁੰਮ ਹੋਈਆਂ ਕੁੰਜੀਆਂ ਲੱਭੋ ਅਤੇ ਉਹਨਾਂ ਦਾ LLM ਰਾਹੀਂ ਅਨੁਵਾਦ ਕਰੋ।
+ਗੁੰਮ ਹੋਈਆਂ ਜਾਂ ਪੁਰਾਣੀਆਂ ਕੁੰਜੀਆਂ ਲੱਭੋ ਅਤੇ ਉਹਨਾਂ ਦਾ LLM ਰਾਹੀਂ ਅਨੁਵਾਦ ਕਰੋ।
```bash
-internationalizer translate # ਸਾਰੇ ਲੋਕੇਲਾਂ ਦਾ ਅਨੁਵਾਦ ਕਰੋ
-internationalizer translate -l fr # ਸਿਰਫ਼ ਫ੍ਰੈਂਚ ਦਾ ਅਨੁਵਾਦ ਕਰੋ
-internationalizer translate --dry-run # API ਕਾਲਾਂ ਤੋਂ ਬਿਨਾਂ ਪੂਰਵ-ਦਰਸ਼ਨ ਕਰੋ
-internationalizer translate --batch-size 20 # ਛੋਟੇ ਬੈਚ
-internationalizer translate --concurrency 2 # ਘੱਟ ਸਮਾਨਾਂਤਰ ਕਾਲਾਂ
+internationalizer translate # translate all locales
+internationalizer translate -l fr # translate French only
+internationalizer translate --dry-run # preview without API calls
+internationalizer translate --adopt-existing # baseline existing translations without API calls
+internationalizer translate --refresh-policy # refresh prompt/style/model-stale entries
+internationalizer translate --batch-size 20 # smaller batches
+internationalizer translate --concurrency 2 # fewer parallel calls
```
+ਅਨੁਵਾਦ ਸਥਿਤੀ ਸੁਤੰਤਰ ਤੌਰ 'ਤੇ ਗੁੰਮ, ਸਰੋਤ-ਪੁਰਾਣੀਆਂ, ਨੀਤੀ-ਪੁਰਾਣੀਆਂ, ਮੌਜੂਦਾ, ਅਤੇ ਮੈਨੁਅਲ ਤੌਰ 'ਤੇ ਸੰਪਾਦਿਤ ਸਥਿਤੀਆਂ ਦੀ ਰਿਪੋਰਟ ਕਰਦੀ ਹੈ, ਇਸ ਲਈ ਕੋਈ ਮੈਨੁਅਲ ਸੰਪਾਦਨ ਕਿਸੇ ਸਰੋਤ ਜਾਂ ਨੀਤੀ ਤਬਦੀਲੀ ਨੂੰ ਲੁਕਾ ਨਹੀਂ ਸਕਦਾ। ਨੀਤੀ-ਪੁਰਾਣੇ ਮੁੱਲਾਂ ਦੀ ਰਿਪੋਰਟ ਕੀਤੀ ਜਾਂਦੀ ਹੈ ਪਰ ਸਿਰਫ਼ `--refresh-policy` ਨਾਲ ਹੀ ਮੁੜ-ਅਨੁਵਾਦ ਕੀਤਾ ਜਾਂਦਾ ਹੈ। ਮੈਨੁਅਲ ਤੌਰ 'ਤੇ ਸੰਪਾਦਿਤ ਕੀਤੇ ਮੁੱਲ ਕਦੇ ਵੀ ਸਵੈਚਲਿਤ ਤੌਰ 'ਤੇ ਓਵਰਰਾਈਟ ਨਹੀਂ ਹੁੰਦੇ। ਸਮੀਖਿਆ ਕੀਤੇ ਅਨੁਵਾਦਾਂ ਵਿੱਚ ਮੈਨੀਫੈਸਟ ਸ਼ਾਮਲ ਕਰਦੇ ਸਮੇਂ ਜਾਂ ਸਮੀਖਿਆ ਕੀਤੇ ਮੈਨੁਅਲ ਸੰਪਾਦਨ ਨੂੰ ਸਪਸ਼ਟ ਤੌਰ 'ਤੇ ਨਵੇਂ ਬੇਸਲਾਈਨ ਵਜੋਂ ਸਵੀਕਾਰ ਕਰਦੇ ਸਮੇਂ `--adopt-existing` ਦੀ ਵਰਤੋਂ ਕਰੋ।
+
### `validate`
-ਗੁੰਮ ਹੋਈਆਂ ਕੁੰਜੀਆਂ, ਵਾਧੂ ਕੁੰਜੀਆਂ, ਅਤੇ ਇੰਟਰਪੋਲੇਸ਼ਨ ਬੇਮੇਲਾਂ ਲਈ ਸਾਰੀਆਂ ਲੋਕੇਲ ਫਾਈਲਾਂ ਦੀ ਜਾਂਚ ਕਰੋ।
+ਸਾਰੀਆਂ ਲੋਕੇਲ ਫਾਈਲਾਂ ਦੀ ਉਹਨਾਂ ਦੇ ਸਰੋਤ ਬੰਡਲਾਂ ਨਾਲ ਜਾਂਚ ਕਰੋ। ਡਿਫੌਲਟ ਪ੍ਰਮਾਣੀਕਰਨ ਬਣਤਰ ਕਵਰੇਜ ਦੀ ਜਾਂਚ ਕਰਦਾ ਹੈ (ਮੌਜੂਦ ਲੋੜੀਂਦੀਆਂ ਟਾਰਗੇਟ ਕੁੰਜੀਆਂ ਦੀ ਪ੍ਰਤੀਸ਼ਤਤਾ), ਵਾਧੂ ਕੁੰਜੀਆਂ ਨੂੰ ਚੇਤਾਵਨੀਆਂ ਵਜੋਂ ਰਿਪੋਰਟ ਕਰਦਾ ਹੈ, ਅਤੇ ਗੁੰਮ ਕੁੰਜੀਆਂ, ਇੰਟਰਪੋਲੇਸ਼ਨ ਬੇਮੇਲਾਂ, ਜਾਂ ਅਵੈਧ ICU MessageFormat ਬਣਤਰ ਲਈ ਅਸਫਲ ਹੁੰਦਾ ਹੈ।
```bash
-internationalizer validate # ਮਨੁੱਖੀ-ਪੜ੍ਹਨਯੋਗ ਆਉਟਪੁੱਟ
-internationalizer validate --json # ਮਸ਼ੀਨ-ਪੜ੍ਹਨਯੋਗ JSON
-internationalizer validate -q # ਸਿਰਫ਼ ਐਗਜ਼ਿਟ ਕੋਡ
+internationalizer validate # human-readable output
+internationalizer validate --json # machine-readable JSON
+internationalizer validate -q # exit code only
+internationalizer validate --strict # enforce translation quality rules
+internationalizer validate --require-state # require current manifest provenance
```
+`--strict` ਅਨੁਵਾਦਿਤ ਕਵਰੇਜ ਦੀ ਵੀ ਰਿਪੋਰਟ ਕਰਦਾ ਹੈ। ਆਪਣੇ ਸਰੋਤ ਦੇ ਸਮਾਨ ਇੱਕ ਭਾਸ਼ਾਈ ਮੁੱਲ ਉਦੋਂ ਤੱਕ ਅਣ-ਅਨੁਵਾਦਿਤ ਰਹਿੰਦਾ ਹੈ ਜਦੋਂ ਤੱਕ ਗਲੋਸਰੀ ਵਿੱਚ ਪੂਰੇ ਮੁੱਲ ਲਈ ਸਪਸ਼ਟ ਤੌਰ 'ਤੇ ਇੱਕੋ ਸਰੋਤ, ਇੱਕੋ ਟਾਰਗੇਟ ਐਂਟਰੀ ਮੌਜੂਦ ਨਾ ਹੋਵੇ; `ignore_case` ਦਾ ਸਤਿਕਾਰ ਕੀਤਾ ਜਾਂਦਾ ਹੈ, ਪਰ ਲੰਬੇ ਮੁੱਲ ਵਿੱਚ ਸ਼ਾਮਲ ਗਲੋਸਰੀ ਸ਼ਬਦ ਛੋਟ ਨਹੀਂ ਹੈ। ਸਖ਼ਤ (Strict) ਮੋਡ ਵਾਧੂ ਕੁੰਜੀਆਂ, ਸਰੋਤ-ਸਮਾਨ ਮੁੱਲਾਂ, ਬਦਲੇ ਹੋਏ ਇੰਟਰਪੋਲੇਸ਼ਨ/HTML/ਕੋਡ/ਮਾਰਕਡਾਊਨ-ਲਿੰਕ ਬਣਤਰ, ਗਲੋਸਰੀ ਉਲੰਘਣਾਵਾਂ, ਅਤੇ ਕੌਂਫਿਗਰ ਕੀਤੇ ਬਹੁਵਚਨ ਰੂਪਾਂ 'ਤੇ ਅਸਫਲ ਹੁੰਦਾ ਹੈ।
+
+`--require-state` `.internationalizer.lock` ਦੇ ਵਿਰੁੱਧ ਹਰੇਕ ਟਾਰਗੇਟ ਦੀ ਪੁਸ਼ਟੀ ਕਰਦਾ ਹੈ। ਇਹ ਉਦੋਂ ਅਸਫਲ ਹੁੰਦਾ ਹੈ ਜਦੋਂ ਕੋਈ ਕੁੰਜੀ ਅਣ-ਟਰੈਕ ਹੁੰਦੀ ਹੈ, ਜਾਂ ਜਦੋਂ ਇਸਦਾ ਰਿਕਾਰਡ ਕੀਤਾ ਸਰੋਤ, ਅਨੁਵਾਦ ਨੀਤੀ, ਜਾਂ ਟਾਰਗੇਟ ਹੈਸ਼ ਪੁਰਾਣਾ ਹੁੰਦਾ ਹੈ। ਇਸਨੂੰ `--strict` ਨਾਲ ਜੋੜਿਆ ਜਾ ਸਕਦਾ ਹੈ।
+
+ਮਨੁੱਖੀ ਅਤੇ JSON ਰਿਪੋਰਟਾਂ ਸਥਿਰ ਖੋਜ ਕੋਡਾਂ ਦੀ ਵਰਤੋਂ ਕਰਦੀਆਂ ਹਨ:
+
+| ਕੋਡ | ਅਰਥ |
+| --- | --- |
+| `missing_key` / `extra_key` | ਸਰੋਤ ਅਤੇ ਟਾਰਗੇਟ ਕੁੰਜੀ ਸੈੱਟ ਵੱਖਰੇ ਹਨ |
+| `blank_translation` | ਇੱਕ ਗੈਰ-ਖਾਲੀ ਸਰੋਤ ਦਾ ਇੱਕ ਖਾਲੀ ਸਖ਼ਤ-ਮੋਡ ਟਾਰਗੇਟ ਹੁੰਦਾ ਹੈ |
+| `source_identical` | ਇੱਕ ਸਖ਼ਤ-ਮੋਡ ਭਾਸ਼ਾਈ ਮੁੱਲ ਅਣ-ਅਨੁਵਾਦਿਤ ਰਹਿੰਦਾ ਹੈ |
+| `protected_structure_mismatch` | ਇੰਟਰਪੋਲੇਸ਼ਨ, HTML, ਕੋਡ, ਜਾਂ ਲਿੰਕ ਬਣਤਰ ਬਦਲ ਗਈ |
+| `glossary_violation` | ਕੋਈ ਪ੍ਰਵਾਨਿਤ ਟਾਰਗੇਟ ਸ਼ਬਦ ਜਾਂ ਰੂਪ ਨਹੀਂ ਮਿਲਿਆ |
+| `plural_form_missing` | ਇੱਕ ਕੌਂਫਿਗਰ ਕੀਤਾ ਲੋਕੇਲ ਬਹੁਵਚਨ ਰੂਪ ਗੈਰ-ਹਾਜ਼ਰ ਹੈ |
+| `icu_message_syntax` | ਇੱਕ ਸਰੋਤ ਜਾਂ ਟਾਰਗੇਟ ICU ਸੁਨੇਹਾ ਖਰਾਬ ਹੈ |
+| `icu_argument_mismatch` | ICU ਆਰਗੂਮੈਂਟ ਦੇ ਨਾਮ, ਕਿਸਮਾਂ, ਜਾਂ ਫਾਰਮੈਟਰ ਸਟਾਈਲ ਵੱਖਰੇ ਹਨ |
+| `icu_selector_mismatch` | ਚੋਣਕਾਰ ਵੱਖਰੇ ਹਨ ਜਾਂ ਇੱਕ ਬਹੁਵਚਨ ਸ਼੍ਰੇਣੀ ਟਾਰਗੇਟ ਲੋਕੇਲ ਲਈ ਅਵੈਧ ਹੈ |
+| `untracked` | ਟਾਰਗੇਟ ਲਈ ਕੋਈ ਮੈਨੀਫੈਸਟ ਰਿਕਾਰਡ ਮੌਜੂਦ ਨਹੀਂ ਹੈ |
+| `source_stale` | ਰਿਕਾਰਡ ਕੀਤੇ ਅਨੁਵਾਦ ਤੋਂ ਬਾਅਦ ਸਰੋਤ ਸਮੱਗਰੀ ਬਦਲ ਗਈ |
+| `policy_stale` | ਤਿਆਰ ਕੀਤਾ ਪ੍ਰੋਂਪਟ ਜਾਂ ਮਾਡਲ ਸੈਟਿੰਗਾਂ ਬਦਲ ਗਈਆਂ |
+| `target_modified` | ਟਾਰਗੇਟ ਸਮੱਗਰੀ ਮੈਨੀਫੈਸਟ ਰਿਕਾਰਡ ਤੋਂ ਵੱਖਰੀ ਹੈ |
+
### `detect`
i18n ਫਰੇਮਵਰਕ ਦਾ ਸਵੈ-ਪਤਾ ਲਗਾਓ ਅਤੇ ਇੱਕ ਕੌਂਫਿਗਰੇਸ਼ਨ ਦਾ ਸੁਝਾਅ ਦਿਓ।
@@ -135,7 +178,7 @@ i18n ਫਰੇਮਵਰਕ ਦਾ ਸਵੈ-ਪਤਾ ਲਗਾਓ ਅਤੇ ਇ
internationalizer detect
```
-ਸਮਰਥਨ ਕਰਦਾ ਹੈ: react-i18next, next-intl, vue-i18n, vanilla JSON, markdown docs।
+ਸਮਰਥਨ ਕਰਦਾ ਹੈ: react-i18next, next-intl, vue-i18n, vanilla JSON, ਮਾਰਕਡਾਊਨ ਦਸਤਾਵੇਜ਼।
### `glossary`
@@ -152,69 +195,119 @@ internationalizer glossary remove --locale fr --source "Dashboard"
ਅਨੁਵਾਦ ਮੈਮੋਰੀ (ਪਹਿਲਾਂ ਅਨੁਵਾਦ ਕੀਤੀਆਂ ਸਟ੍ਰਿੰਗਾਂ ਦਾ JSONL ਕੈਸ਼) ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ।
```bash
-internationalizer tm stats # ਰਿਕਾਰਡ ਗਿਣਤੀ ਦਿਖਾਓ
-internationalizer tm export # JSON ਵਜੋਂ ਡੰਪ ਕਰੋ
-internationalizer tm clear --force # ਸਾਰੇ ਰਿਕਾਰਡ ਮਿਟਾਓ
+internationalizer tm stats # show record counts
+internationalizer tm export # dump as JSON
+internationalizer tm clear --force # delete all records
```
+
## ਕੌਂਫਿਗਰੇਸ਼ਨ ਹਵਾਲਾ
```yaml
# .internationalizer.yml
-# ਸਰੋਤ ਭਾਸ਼ਾ (ਡਿਫੌਲਟ: en)
+# Source language (default: en)
source_locale: en
-# ਅਨੁਵਾਦ ਕਰਨ ਲਈ ਭਾਸ਼ਾਵਾਂ (ਲੋੜੀਂਦਾ)
-target_locales: [fr, de, es, ja, zh-CN, ar]
-
-# ਸਰੋਤ ਲੋਕੇਲ ਫਾਈਲ ਦਾ ਮਾਰਗ (ਲੋੜੀਂਦਾ)
-source_path: locales/en.json
-
-# LLM ਪ੍ਰਦਾਤਾ ਸੈਟਿੰਗਾਂ
+# Languages to translate into (required)
+target_locales: [fr, de, es, ja, yue, zh-CN, zh-TW, ar]
+
+# One or more source-to-target mappings (required).
+# {locale} is replaced with each configured target locale.
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
+ - id: docs
+ source: README.md
+ target: docs/i18n/{locale}.md
+ format: markdown
+
+# Backward compatibility: source_path still maps targets to sibling files
+# such as locales/fr.json. Prefer bundles for new projects.
+# source_path: locales/en.json
+
+# LLM provider settings
llm:
- # ਪ੍ਰਦਾਤਾ: "anthropic", "openai", "gemini", ਜਾਂ "openrouter" (ਡਿਫੌਲਟ: gemini)
+ # Provider: "anthropic", "openai", "gemini", or "openrouter" (default: gemini)
provider: gemini
- # ਮਾਡਲ ਨਾਮ ਪ੍ਰਦਾਤਾ ਦੁਆਰਾ ਡਿਫੌਲਟ:
+ # Model name defaults by provider:
# anthropic: claude-opus-5
- # openai: gpt-5.6-luna
+ # openai: gpt-5.6-luna (reasoning effort defaults to max)
# gemini: gemini-3.8-flash
# openrouter: deepseek/deepseek-v4-pro-0813
model: gemini-3.8-flash
- # API ਕੁੰਜੀ ਵਾਲਾ ਵਾਤਾਵਰਣ ਵੇਰੀਏਬਲ
+ # Environment variable containing the API key
api_key_env: GOOGLE_AI_STUDIO_API_KEY
- # OpenAI-ਅਨੁਕੂਲ ਐਂਡਪੁਆਇੰਟਾਂ ਲਈ ਬੇਸ URL (ਵਿਕਲਪਿਕ)
+ # Base URL for OpenAI-compatible endpoints (optional)
# base_url: https://api.openai.com
-# ਪ੍ਰਤੀ LLM ਕਾਲ ਕੁੰਜੀਆਂ (ਡਿਫੌਲਟ: 40)
+ # OpenAI GPT-5-series Responses API reasoning effort
+ # (default: max for the OpenAI provider)
+ reasoning_effort: max
+
+ # Optional LLM settings for individual target locales. An override using the
+ # global provider inherits unspecified global settings. A different provider
+ # uses that provider's defaults for unspecified settings.
+ locale_overrides:
+ yue:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-CN:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-TW:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+
+# Keys per LLM call (default: 40)
batch_size: 40
-# ਸਮਾਨਾਂਤਰ LLM ਕਾਲਾਂ (ਡਿਫੌਲਟ: 4)
+# Parallel LLM calls (default: 4)
concurrency: 4
-# ਪ੍ਰਤੀ-ਲੋਕੇਲ ਸਟਾਈਲ ਗਾਈਡ ਮਾਰਕਡਾਊਨ ਫਾਈਲਾਂ ਵਾਲੀ ਡਾਇਰੈਕਟਰੀ (ਡਿਫੌਲਟ: style-guides)
+# Directory containing per-locale style guide Markdown files (default: style-guides)
style_guides_dir: style-guides
-# ਪ੍ਰਤੀ-ਲੋਕੇਲ ਗਲੋਸਰੀ JSON ਫਾਈਲਾਂ ਵਾਲੀ ਡਾਇਰੈਕਟਰੀ (ਡਿਫੌਲਟ: glossary)
+# Directory containing per-locale glossary JSON files (default: glossary)
glossary_dir: glossary
-# ਅਨੁਵਾਦ ਮੈਮੋਰੀ ਫਾਈਲ ਦਾ ਮਾਰਗ (ਡਿਫੌਲਟ: .internationalizer/tm.jsonl)
+# Path to translation memory file (default: .internationalizer/tm.jsonl)
tm_path: .internationalizer/tm.jsonl
+
+# Versioned source, policy, target, and provenance state
+# (default: .internationalizer.lock; commit this file)
+manifest_path: .internationalizer.lock
+
+# Optional translation and strict-validation rules
+validation:
+ plural_style: i18next-v4 # generate and validate target-locale plural forms
```
+ਲੋਕੇਲ ਪਛਾਣਕਰਤਾ ਚੰਗੀ ਤਰ੍ਹਾਂ ਬਣੇ BCP 47 ਟੈਗ ਹੋਣੇ ਚਾਹੀਦੇ ਹਨ ਜਿਵੇਂ ਕਿ `fr`, `pt-BR`, ਜਾਂ `sr-Latn-RS`। ਪ੍ਰਮਾਣਿਕ-ਬਰਾਬਰ ਟਾਰਗੇਟ ਲੋਕੇਲ ਡੁਪਲੀਕੇਟ ਵਜੋਂ ਰੱਦ ਕੀਤੇ ਜਾਂਦੇ ਹਨ, ਅਤੇ ਲੋਕੇਲ-ਵਿਸ਼ੇਸ਼ ਪ੍ਰਦਾਤਾ ਓਵਰਰਾਈਡ ਪ੍ਰਮਾਣਿਕ-ਬਰਾਬਰ ਸਪੈਲਿੰਗ ਨਾਲ ਮੇਲ ਖਾਂਦੇ ਹਨ। ਉਪਰੋਕਤ ਉਦਾਹਰਨ ਵਿੱਚ, ਬਿਨਾਂ ਕਿਸੇ ਓਵਰਰਾਈਡ ਵਾਲੇ ਲੋਕੇਲ—ਜਾਪਾਨੀ ਸਮੇਤ—ਗਲੋਬਲ Gemini ਕੌਂਫਿਗਰੇਸ਼ਨ ਨੂੰ ਵਿਰਾਸਤ ਵਿੱਚ ਪ੍ਰਾਪਤ ਕਰਦੇ ਹਨ।
+
+ICU MessageFormat ਮੁੱਲਾਂ ਨੂੰ ਬਣਤਰ ਦੇ ਅਧਾਰ 'ਤੇ ਪਾਰਸ ਕੀਤਾ ਜਾਂਦਾ ਹੈ। ਸਰਲ ਆਰਗੂਮੈਂਟ, `select`, `plural`, `selectordinal`, `number`, `date`, ਅਤੇ `time` ਸਮਰਥਿਤ ਹਨ, ਜਿਸ ਵਿੱਚ ਨੇਸਟਡ ਸੁਨੇਹੇ, ਬਹੁਵਚਨ ਆਫਸੈੱਟ, ਸਹੀ-ਸੰਖਿਆ ਚੋਣਕਾਰ, ਅਤੇ `#` ਸ਼ਾਮਲ ਹਨ। ਪ੍ਰਮਾਣੀਕਰਨ ਸਿੰਟੈਕਸ, ਆਰਗੂਮੈਂਟ ਕਿਸਮਾਂ ਅਤੇ ਫਾਰਮੈਟਰ ਸਟਾਈਲ, ਬਹੁਵਚਨ ਆਫਸੈੱਟ, ਚੋਣ ਸ਼ਾਖਾ ਪਛਾਣ, ਅਤੇ ਟਾਰਗੇਟ-ਲੋਕੇਲ CLDR ਬਹੁਵਚਨ ਸ਼੍ਰੇਣੀਆਂ ਦੀ ਜਾਂਚ ਕਰਦਾ ਹੈ। ਪ੍ਰਦਾਤਾ ਆਉਟਪੁੱਟ ਜੋ ਇਹਨਾਂ ਨਿਯਮਾਂ ਨੂੰ ਤੋੜਦੀ ਹੈ, ਇੱਕ ਲੋਕੇਲ ਫਾਈਲ ਜਾਂ ਅਨੁਵਾਦ-ਮੈਮੋਰੀ ਰਿਕਾਰਡ ਲਿਖੇ ਜਾਣ ਤੋਂ ਪਹਿਲਾਂ ਰੱਦ ਕਰ ਦਿੱਤੀ ਜਾਂਦੀ ਹੈ।
+
+`i18next-v4` ਦੇ ਨਾਲ, ਮਾਨਤਾ ਪ੍ਰਾਪਤ ਸਰੋਤ ਬਹੁਵਚਨ ਪਰਿਵਾਰਾਂ ਦਾ ਅਨੁਵਾਦ ਦੌਰਾਨ ਟਾਰਗੇਟ ਲੋਕੇਲ ਦੀਆਂ CLDR ਸ਼੍ਰੇਣੀਆਂ ਵਿੱਚ ਵਿਸਤਾਰ ਕੀਤਾ ਜਾਂਦਾ ਹੈ। ਇੱਕ ਸਿਰਫ਼-ਟਾਰਗੇਟ ਸ਼੍ਰੇਣੀ ਸਰੋਤ ਪਰਿਵਾਰ ਦੇ `_other` ਮੁੱਲ ਨੂੰ ਆਪਣੇ ਅਨੁਵਾਦ ਟੈਂਪਲੇਟ ਵਜੋਂ ਵਰਤਦੀ ਹੈ। ਸਖ਼ਤ ਪ੍ਰਮਾਣੀਕਰਨ ਲਈ ਉਹਨਾਂ ਟਾਰਗੇਟ ਸ਼੍ਰੇਣੀਆਂ ਦੀ ਲੋੜ ਹੁੰਦੀ ਹੈ; ਸਿਰਫ਼-ਸਰੋਤ ਸ਼੍ਰੇਣੀਆਂ ਉਹਨਾਂ ਟਾਰਗੇਟ ਲੋਕੇਲਾਂ ਲਈ ਵਿਕਲਪਿਕ ਹਨ ਜੋ ਉਹਨਾਂ ਦੀ ਵਰਤੋਂ ਨਹੀਂ ਕਰਦੇ ਹਨ।
+
+
## ਸਟਾਈਲ ਗਾਈਡਾਂ
-ਸਟਾਈਲ ਗਾਈਡਾਂ ਮਾਰਕਡਾਊਨ ਫਾਈਲਾਂ ਹਨ ਜੋ LLM ਅਨੁਵਾਦ ਪ੍ਰੋਂਪਟ ਵਿੱਚ ਇੰਜੈਕਟ ਕੀਤੀਆਂ ਜਾਂਦੀਆਂ ਹਨ। ਉਹ ਟੋਨ, ਰਸਮੀਪਣ, ਟਾਈਪੋਗ੍ਰਾਫੀ, ਅਤੇ ਹੋਰ ਭਾਸ਼ਾ-ਵਿਸ਼ੇਸ਼ ਪਰੰਪਰਾਵਾਂ ਨੂੰ ਕੰਟਰੋਲ ਕਰਦੀਆਂ ਹਨ।
+ਸਟਾਈਲ ਗਾਈਡਾਂ ਮਾਰਕਡਾਊਨ ਫਾਈਲਾਂ ਹਨ ਜੋ LLM ਅਨੁਵਾਦ ਪ੍ਰੋਂਪਟ ਵਿੱਚ ਸ਼ਾਮਲ ਕੀਤੀਆਂ ਜਾਂਦੀਆਂ ਹਨ। ਉਹ ਟੋਨ, ਰਸਮੀਪਣ, ਟਾਈਪੋਗ੍ਰਾਫੀ, ਅਤੇ ਹੋਰ ਭਾਸ਼ਾ-ਵਿਸ਼ੇਸ਼ ਪਰੰਪਰਾਵਾਂ ਨੂੰ ਕੰਟਰੋਲ ਕਰਦੀਆਂ ਹਨ।
```
style-guides/
- _conventions.md # ਸਾਰੀਆਂ ਭਾਸ਼ਾਵਾਂ ਲਈ ਸਾਂਝੇ ਨਿਯਮ
- fr.md # ਫ੍ਰੈਂਚ-ਵਿਸ਼ੇਸ਼ ਨਿਯਮ
- ja.md # ਜਾਪਾਨੀ-ਵਿਸ਼ੇਸ਼ ਨਿਯਮ
- ar.md # ਅਰਬੀ-ਵਿਸ਼ੇਸ਼ ਨਿਯਮ
+ _conventions.md # shared rules for all languages
+ fr.md # French-specific rules
+ ja.md # Japanese-specific rules
+ ar.md # Arabic-specific rules
```
### ਸਾਂਝੀਆਂ ਪਰੰਪਰਾਵਾਂ (`_conventions.md`)
@@ -223,10 +316,13 @@ style-guides/
### ਪ੍ਰਤੀ-ਭਾਸ਼ਾ ਗਾਈਡਾਂ (`{locale}.md`)
-ਭਾਸ਼ਾ-ਵਿਸ਼ੇਸ਼ ਨਿਯਮ ਪਰਿਭਾਸ਼ਿਤ ਕਰੋ: ਰਸਮੀਪਣ ਰਜਿਸਟਰ (tu ਬਨਾਮ vous), ਵਿਸ਼ਰਾਮ ਚਿੰਨ੍ਹ (guillemets, ਉਲਟੇ ਪ੍ਰਸ਼ਨ ਚਿੰਨ੍ਹ), ਬਹੁਵਚਨ ਰੂਪ, ਮਿਤੀ/ਨੰਬਰ ਫਾਰਮੈਟਿੰਗ, ਅਤੇ ਇੱਕ ਸ਼ਬਦਾਵਲੀ ਗਲੋਸਰੀ।
+ਭਾਸ਼ਾ-ਵਿਸ਼ੇਸ਼ ਨਿਯਮ ਪਰਿਭਾਸ਼ਿਤ ਕਰੋ: ਰਸਮੀਪਣ ਰਜਿਸਟਰ (tu ਬਨਾਮ vous), ਵਿਸ਼ਰਾਮ ਚਿੰਨ੍ਹ (guillemets, ਉਲਟੇ ਪ੍ਰਸ਼ਨ ਚਿੰਨ੍ਹ), ਬਹੁਵਚਨ ਰੂਪ, ਮਿਤੀ/ਸੰਖਿਆ ਫਾਰਮੈਟਿੰਗ, ਅਤੇ ਇੱਕ ਸ਼ਬਦਾਵਲੀ ਗਲੋਸਰੀ।
+
+ਸਟਾਈਲ ਗਾਈਡਾਂ ਸਥਾਈ ਨੀਤੀ ਇਨਪੁਟ ਹਨ, ਨਾ ਕਿ ਤਿਆਰ ਕੀਤਾ ਆਉਟਪੁੱਟ। Internationalizer ਉਹਨਾਂ ਨੂੰ ਪੜ੍ਹਦਾ ਹੈ ਪਰ ਕਦੇ ਵੀ ਮੁੜ ਨਹੀਂ ਲਿਖਦਾ। ਉਹਨਾਂ ਦੀ ਸਮੱਗਰੀ ਨੂੰ ਗਲੋਸਰੀ ਅਤੇ ਪ੍ਰੋਂਪਟ ਕੰਟਰੈਕਟ ਤੋਂ ਵੱਖਰੇ ਤੌਰ 'ਤੇ ਹੈਸ਼ ਕੀਤਾ ਜਾਂਦਾ ਹੈ, ਤਾਂ ਜੋ ਇੱਕ ਐਪਲੀਕੇਸ਼ਨ ਕੋਡ ਤਬਦੀਲੀ ਅਨੁਵਾਦ ਨੂੰ ਪੁਰਾਣਾ ਨਾ ਬਣਾਏ। ਕਿਸੇ ਗਾਈਡ ਨੂੰ ਸੰਪਾਦਿਤ ਕਰਨਾ ਜਾਣਬੁੱਝ ਕੇ ਉਸ ਲੋਕੇਲ ਨੂੰ ਨੀਤੀ ਸਮੀਖਿਆ ਲਈ ਚਿੰਨ੍ਹਿਤ ਕਰਦਾ ਹੈ; ਅੰਦਰੂਨੀ ਪ੍ਰੋਂਪਟ ਸ਼ਬਦਾਵਲੀ ਨੂੰ ਬਦਲਣਾ ਅਜਿਹਾ ਨਹੀਂ ਕਰਦਾ, ਜਦੋਂ ਤੱਕ ਪ੍ਰੋਂਪਟ ਕੰਟਰੈਕਟ ਵਰਜਨ ਵੀ ਨਹੀਂ ਬਦਲਦਾ।
-ਕੰਮ ਕਰਨ ਵਾਲੀ ਉਦਾਹਰਨ ਲਈ [`examples/react-app/style-guides/`](examples/react-app/style-guides/) ਦੇਖੋ।
+ਕਾਰਜਸ਼ੀਲ ਉਦਾਹਰਨ ਲਈ [`examples/react-app/style-guides/`](../../examples/react-app/style-guides/) ਦੇਖੋ।
+
## ਗਲੋਸਰੀ ਫਾਰਮੈਟ
ਗਲੋਸਰੀ ਫਾਈਲਾਂ `{glossary_dir}/{locale}.json` ਵਿੱਚ ਸਟੋਰ ਕੀਤੀਆਂ JSON ਐਰੇ ਹਨ:
@@ -236,33 +332,40 @@ style-guides/
{
"source": "Dashboard",
"target": "Tableau de bord",
+ "variants": ["Panneau de contrôle"],
+ "enforcement": "error",
"ignore_case": false,
"whole_word": true
}
]
```
-ਸ਼ਬਦਾਂ ਨੂੰ LLM ਪ੍ਰੋਂਪਟ ਵਿੱਚ ਇੱਕ ਸ਼ਬਦਾਵਲੀ ਸਾਰਣੀ ਵਜੋਂ ਇੰਜੈਕਟ ਕੀਤਾ ਜਾਂਦਾ ਹੈ, ਜੋ ਤੁਹਾਡੀ ਐਪਲੀਕੇਸ਼ਨ ਵਿੱਚ ਮੁੱਖ ਸ਼ਬਦਾਂ ਦੇ ਇਕਸਾਰ ਅਨੁਵਾਦ ਨੂੰ ਯਕੀਨੀ ਬਣਾਉਂਦਾ ਹੈ।
+`variants` ਹੋਰ ਪ੍ਰਵਾਨਿਤ ਟਾਰਗੇਟ ਰੂਪਾਂ ਨੂੰ ਸੂਚੀਬੱਧ ਕਰਦਾ ਹੈ। `enforcement` ਸ਼ਾਇਦ `error`, `warning` ਹੋ ਸਕਦਾ ਹੈ, ਜਾਂ ਡਿਫੌਲਟ ਤਰੁੱਟੀ ਵਿਵਹਾਰ ਲਈ ਛੱਡਿਆ ਜਾ ਸਕਦਾ ਹੈ। ਸ਼ਬਦਾਂ ਨੂੰ LLM ਪ੍ਰੋਂਪਟ ਵਿੱਚ ਇੱਕ ਸ਼ਬਦਾਵਲੀ ਸਾਰਣੀ ਵਜੋਂ ਸ਼ਾਮਲ ਕੀਤਾ ਜਾਂਦਾ ਹੈ, ਜੋ ਤੁਹਾਡੀ ਐਪਲੀਕੇਸ਼ਨ ਵਿੱਚ ਇਕਸਾਰ ਅਨੁਵਾਦ ਨੂੰ ਯਕੀਨੀ ਬਣਾਉਂਦਾ ਹੈ। ਇੱਕ ਸਹੀ ਐਂਟਰੀ ਜਿਵੇਂ ਕਿ `{"source":"API","target":"API"}` ਉਸ ਪੂਰੇ ਸਰੋਤ-ਸਮਾਨ ਮੁੱਲ ਨੂੰ ਸਖ਼ਤ ਅਣ-ਅਨੁਵਾਦਿਤ ਮੁੱਲ ਖੋਜਾਂ ਤੋਂ ਵੀ ਛੋਟ ਦਿੰਦੀ ਹੈ; ਇਹ ਕੇਵਲ `API` ਵਾਲੇ ਇੱਕ ਲੰਬੇ ਮੁੱਲ ਨੂੰ ਛੋਟ ਨਹੀਂ ਦਿੰਦੀ।
+
## ਅਨੁਵਾਦ ਮੈਮੋਰੀ
ਅਨੁਵਾਦ ਮੈਮੋਰੀ ਨੂੰ ਇੱਕ JSONL ਫਾਈਲ (ਪ੍ਰਤੀ ਲਾਈਨ ਇੱਕ JSON ਰਿਕਾਰਡ) ਵਜੋਂ ਸਟੋਰ ਕੀਤਾ ਜਾਂਦਾ ਹੈ। ਹਰੇਕ ਰਿਕਾਰਡ ਵਿੱਚ ਸ਼ਾਮਲ ਹਨ:
-- ਸਰੋਤ ਕੁੰਜੀ ਅਤੇ ਮੁੱਲ
-- ਅਨੁਵਾਦ ਕੀਤਾ ਮੁੱਲ
-- ਸਰੋਤ ਮੁੱਲ ਦਾ ਇੱਕ SHA-256 ਹੈਸ਼
+- ਬੰਡਲ, ਕੁੰਜੀ, ਸਰੋਤ ਮੁੱਲ, ਅਨੁਵਾਦਿਤ ਮੁੱਲ, ਅਤੇ ਪ੍ਰਮਾਣਿਕ ਟਾਰਗੇਟ ਲੋਕੇਲ
+- ਸਰੋਤ, ਸਟਾਈਲ-ਗਾਈਡ, ਗਲੋਸਰੀ, ਪ੍ਰੋਂਪਟ-ਕੰਟਰੈਕਟ, ਅਤੇ ਸੰਯੁਕਤ ਨੀਤੀ ਹੈਸ਼
+- ਪ੍ਰਦਾਤਾ ਅਤੇ ਮਾਡਲ ਜਿਸਨੇ ਅਨੁਵਾਦ ਤਿਆਰ ਕੀਤਾ
- ਇੱਕ ਟਾਈਮਸਟੈਂਪ
-ਅਗਲੀਆਂ ਦੌੜਾਂ 'ਤੇ, ਨਾ ਬਦਲੀਆਂ ਸਟ੍ਰਿੰਗਾਂ ਨੂੰ LLM ਨੂੰ ਕਾਲ ਕੀਤੇ ਬਿਨਾਂ TM ਕੈਸ਼ ਤੋਂ ਸਰਵ ਕੀਤਾ ਜਾਂਦਾ ਹੈ, ਜਿਸ ਨਾਲ ਸਮਾਂ ਅਤੇ API ਲਾਗਤਾਂ ਦੋਵਾਂ ਦੀ ਬਚਤ ਹੁੰਦੀ ਹੈ। TM ਫਾਈਲ git-ਅਨੁਕੂਲ ਹੈ ਅਤੇ ਤੁਹਾਡੀਆਂ ਲੋਕੇਲ ਫਾਈਲਾਂ ਦੇ ਨਾਲ ਕਮਿਟ ਕੀਤੀ ਜਾ ਸਕਦੀ ਹੈ।
+ਬਾਅਦ ਦੀਆਂ ਦੌੜਾਂ 'ਤੇ, ਇੱਕੋ ਸਰੋਤ ਅਤੇ ਨੀਤੀ ਹੈਸ਼ ਵਾਲੀਆਂ ਸਟ੍ਰਿੰਗਾਂ ਨੂੰ LLM ਨੂੰ ਕਾਲ ਕੀਤੇ ਬਿਨਾਂ ਕੈਸ਼ ਤੋਂ ਸਰਵ ਕੀਤਾ ਜਾਂਦਾ ਹੈ। ਡਿਫੌਲਟ ਮਾਰਗ ਅਣਡਿੱਠ ਕੀਤੀ ਗਈ `.internationalizer/` ਡਾਇਰੈਕਟਰੀ ਅਧੀਨ ਹੈ, ਇਸ ਲਈ ਇਹ ਇੱਕ ਸਥਾਨਕ ਕੈਸ਼ ਰਹਿੰਦਾ ਹੈ। ਜੇਕਰ ਤੁਹਾਡਾ ਪ੍ਰੋਜੈਕਟ ਜਾਣਬੁੱਝ ਕੇ ਅਨੁਵਾਦ ਮੈਮੋਰੀ ਸਾਂਝੀ ਕਰਦਾ ਹੈ ਤਾਂ `tm_path` ਨੂੰ ਇੱਕ ਟ੍ਰੈਕ ਕੀਤੇ ਸਥਾਨ 'ਤੇ ਸੈੱਟ ਕਰੋ। ਸਮੀਖਿਆ ਯੋਗ `.internationalizer.lock` ਮੈਨੀਫੈਸਟ ਨੂੰ ਵੱਖਰੇ ਤੌਰ 'ਤੇ ਵਰਜਨ ਕੀਤਾ ਗਿਆ ਹੈ।
+
## ਸਮਰਥਿਤ ਫਾਰਮੈਟ
| ਫਾਰਮੈਟ | ਐਕਸਟੈਂਸ਼ਨਾਂ | ਮੋਡ |
|--------|-----------|------|
-| JSON | `.json` | Key-value (nested, dot-notation flattened) |
+| JSON | `.json` | Key-value (ਨੇਸਟਡ, ਡਾਟ-ਨੋਟੇਸ਼ਨ ਫਲੈਟਨਡ) |
| YAML | `.yml`, `.yaml` | Key-value (ਟਿੱਪਣੀਆਂ ਅਤੇ ਕ੍ਰਮ ਨੂੰ ਸੁਰੱਖਿਅਤ ਰੱਖਦਾ ਹੈ) |
-| Markdown | `.md`, `.mdx` | ਪੂਰੇ-ਦਸਤਾਵੇਜ਼ ਦਾ ਅਨੁਵਾਦ |
+| Markdown | `.md`, `.mdx` | ਪ੍ਰੀਐਂਬਲ ਅਤੇ H2-ਪੱਧਰ ਦੇ ਭਾਗ |
+
+ਮਾਰਕਡਾਊਨ ਟਾਰਗੇਟਾਂ ਵਿੱਚ H2 ਭਾਗਾਂ ਤੋਂ ਪਹਿਲਾਂ ਅਦਿੱਖ `internationalizer:unit` ਟਿੱਪਣੀਆਂ ਹੁੰਦੀਆਂ ਹਨ। ਇਹ ਸਥਿਰ ਮਾਰਕਰ Internationalizer ਨੂੰ ਗੈਰ-ਸੰਬੰਧਿਤ ਭਾਗਾਂ ਦਾ ਮੁੜ-ਅਨੁਵਾਦ ਕੀਤੇ ਬਿਨਾਂ ਇੱਕ ਸਰੋਤ ਭਾਗ ਨੂੰ ਜੋੜਨ, ਲਿਜਾਣ ਜਾਂ ਸੰਪਾਦਿਤ ਕਰਨ ਦਿੰਦੇ ਹਨ। ਮੌਜੂਦਾ ਅਣ-ਚਿੰਨ੍ਹਿਤ ਦਸਤਾਵੇਜ਼ ਉਹਨਾਂ ਦੇ ਅਗਲੇ ਸਫਲ ਅੱਪਡੇਟ 'ਤੇ ਮਾਰਕਰ ਪ੍ਰਾਪਤ ਕਰਦੇ ਹਨ।
+
## ਪ੍ਰੋਜੈਕਟ ਕਿਸਮ ਦੀ ਪਛਾਣ
`internationalizer detect` ਇਹਨਾਂ ਦੀ ਜਾਂਚ ਕਰਕੇ ਤੁਹਾਡੇ i18n ਸੈੱਟਅੱਪ ਦੀ ਪਛਾਣ ਕਰਦਾ ਹੈ:
@@ -271,26 +374,32 @@ style-guides/
- ਆਮ ਲੋਕੇਲ ਪੈਟਰਨਾਂ ਨਾਲ ਮੇਲ ਖਾਂਦੀਆਂ ਡਾਇਰੈਕਟਰੀ ਬਣਤਰਾਂ
- ਫਾਈਲ ਐਕਸਟੈਂਸ਼ਨਾਂ ਅਤੇ ਨਾਮਕਰਨ ਪਰੰਪਰਾਵਾਂ
+
## ਆਰਕੀਟੈਕਚਰ
```
-cmd/internationalizer/ CLI ਐਂਟਰੀ ਪੁਆਇੰਟ ਅਤੇ ਕਮਾਂਡ ਪਰਿਭਾਸ਼ਾਵਾਂ
+cmd/internationalizer/ CLI entry point and command definitions
internal/
- config/ ਡਿਫੌਲਟਾਂ ਨਾਲ YAML ਕੌਂਫਿਗ ਲੋਡਿੰਗ
- detect/ ਪ੍ਰੋਜੈਕਟ ਕਿਸਮ ਦਾ ਸਵੈ-ਪਤਾ ਲਗਾਉਣਾ
- formats/ ਫਾਰਮੈਟ ਪਾਰਸਰ (JSON, YAML, Markdown)
- glossary/ ਪ੍ਰਤੀ-ਲੋਕੇਲ ਗਲੋਸਰੀ ਪ੍ਰਬੰਧਨ
- llm/ LLM ਪ੍ਰਦਾਤਾ ਇੰਟਰਫੇਸ + ਲਾਗੂਕਰਨ
- anthropic.go Anthropic Claude ਬੈਕਐਂਡ
- openai.go OpenAI / ਅਨੁਕੂਲ ਬੈਕਐਂਡ
- gemini.go AI Studio ਬੈਕਐਂਡ ਰਾਹੀਂ Google Gemini
- OpenRouter ਕਸਟਮ base_url ਨਾਲ openai.go ਦੀ ਵਰਤੋਂ ਕਰਦਾ ਹੈ
- styleguide/ ਸਟਾਈਲ ਗਾਈਡ ਲੋਡਰ
- tm/ JSONL ਅਨੁਵਾਦ ਮੈਮੋਰੀ
- translate/ ਅਨੁਵਾਦ ਆਰਕੈਸਟ੍ਰੇਟਰ
- validate/ ਲੋਕੇਲ ਪ੍ਰਮਾਣਿਕਤਾ ਅਤੇ ਡਿਫਿੰਗ
+ config/ YAML config loading with defaults
+ detect/ Project type auto-detection
+ formats/ Format parsers (JSON, YAML, Markdown)
+ glossary/ Per-locale glossary management
+ llm/ LLM provider interface + implementations
+ anthropic.go Anthropic Claude backend
+ openai.go OpenAI / compatible backend
+ gemini.go Google Gemini via AI Studio backend
+ OpenRouter uses openai.go with custom base_url
+ locale/ BCP 47 identity and CLDR plural categories
+ message/ ICU MessageFormat parser and structural comparison
+ policy/ Stable translation-policy hashing
+ state/ Versioned translation manifest
+ styleguide/ Style guide loader
+ tm/ JSONL translation memory
+ translate/ Translation orchestrator
+ validate/ Locale validation and diffing
```
+
## ਵਿਕਲਪਾਂ ਨਾਲ ਤੁਲਨਾ
| ਵਿਸ਼ੇਸ਼ਤਾ | Internationalizer | i18next | Crowdin | ਆਮ LLM |
@@ -304,11 +413,14 @@ internal/
| ਕੋਈ SaaS ਨਿਰਭਰਤਾ ਨਹੀਂ | ਹਾਂ | ਹਾਂ | ਨਹੀਂ | ਬਦਲਦਾ ਹੈ |
| ਓਪਨ ਸੋਰਸ (AGPL-3.0) | ਹਾਂ | ਹਾਂ | ਨਹੀਂ | ਬਦਲਦਾ ਹੈ |
+
## ਲਾਇਸੰਸ
-[AGPL-3.0](LICENSE)
+[AGPL-3.0](../../LICENSE)
-## ਯੋਗਦਾਨ ਪਾਉਣਾ
+ਨਿਰਭਰਤਾ ਨੋਟਿਸਾਂ ਲਈ [THIRD_PARTY_NOTICES.md](../../THIRD_PARTY_NOTICES.md) ਦੇਖੋ।
-ਵਿਕਾਸ ਸੈੱਟਅੱਪ ਅਤੇ ਦਿਸ਼ਾ-ਨਿਰਦੇਸ਼ਾਂ ਲਈ [CONTRIBUTING.md](CONTRIBUTING.md) ਦੇਖੋ। ਸਾਰੇ ਯੋਗਦਾਨਾਂ ਲਈ DCO ਸਾਈਨ-ਆਫ ਦੀ ਲੋੜ ਹੁੰਦੀ ਹੈ。
+
+## ਯੋਗਦਾਨ ਪਾਉਣਾ
+ਵਿਕਾਸ ਸੈੱਟਅੱਪ ਅਤੇ ਦਿਸ਼ਾ-ਨਿਰਦੇਸ਼ਾਂ ਲਈ [CONTRIBUTING.md](../../CONTRIBUTING.md) ਦੇਖੋ। ਸਾਰੇ ਯੋਗਦਾਨਾਂ ਲਈ DCO ਸਾਈਨ-ਆਫ ਦੀ ਲੋੜ ਹੁੰਦੀ ਹੈ।
diff --git a/docs/i18n/pl.md b/docs/i18n/pl.md
index c599b04..aff14d8 100644
--- a/docs/i18n/pl.md
+++ b/docs/i18n/pl.md
@@ -6,32 +6,34 @@
# Internationalizer
-Oparty na AI potok internacjonalizacji dla projektów programistycznych. Tłumacz, weryfikuj i zarządzaj plikami i18n przy użyciu LLM.
+Natywny dla AI potok internacjonalizacji dla projektów programistycznych. Tłumacz, weryfikuj i zarządzaj plikami i18n przy użyciu LLM.
[](https://github.com/Tom-R-Main/Internationalizer/actions/workflows/ci.yml)
-[](LICENSE)
+[](../../LICENSE)
-العربية · বাংলা · Deutsch · Español · Français · हिन्दी · Indonesia · 日本語 · ਪੰਜਾਬੀ · Português · Русский · తెలుగు
ไทย · Українська · 粵語 · 简体中文 · 繁體中文
+العربية · বাংলা · Čeština · Dansk · Deutsch · Ελληνικά · Español · Suomi · Français · עברית · हिन्दी · Indonesia · Italiano · 日本語 · 한국어 · Bahasa Melayu
Nederlands · ਪੰਜਾਬੀ · Polski · Português · Română · Русский · Svenska · తెలుగు · ไทย · Türkçe · Українська · Tiếng Việt · 粵語 · 简体中文 · 繁體中文
---
+
## Dlaczego Internationalizer?
-Większość narzędzi i18n to biblioteki wykonawcze (i18next, react-intl) lub platformy SaaS do zarządzania kluczami (Crowdin, Lokalise). Żadne z nich nie rozwiązuje dobrze rzeczywistego problemu tłumaczenia:
+Większość narzędzi i18n to biblioteki uruchomieniowe (i18next, react-intl) albo platformy SaaS do zarządzania kluczami (Crowdin, Lokalise). Żadne z nich nie rozwiązuje dobrze rzeczywistego problemu tłumaczenia:
- **Ręczne tłumaczenie** nie skaluje się powyżej kilku języków
-- **API tłumaczenia maszynowego** (Google Translate, DeepL) ignorują Twoją terminologię, ton i konwencje UI
-- **Ogólne tłumaczenie LLM** działa lepiej, ale bez glosariuszy i przewodników po stylu otrzymujesz niespójne wyniki
+- **Interfejsy API tłumaczenia maszynowego** (Google Translate, DeepL) ignorują Twoją terminologię, ton i konwencje interfejsu użytkownika
+- **Ogólne tłumaczenie za pomocą LLM** działa lepiej, ale bez glosariuszy i przewodników po stylu daje niespójne wyniki
-Internationalizer jest inny. To **potok CLI**, który łączy tłumaczenie LLM z:
+Internationalizer działa inaczej. To **potok CLI**, który łączy tłumaczenie LLM z:
- **Glosariuszami dla poszczególnych języków** — wymuszają spójną terminologię w całej aplikacji
-- **Przewodnikami po stylu dla poszczególnych języków** — kontrolują ton, formalność, liczby mnogie i typografię
-- **Pamięcią tłumaczeniową** — pomijają niezmienione ciągi znaków, oszczędzając pieniądze na wywołaniach API
-- **Weryfikacją kluczy** — wychwytują brakujące tłumaczenia i niezgodności interpolacji przed wdrożeniem
+- **Przewodnikami po stylu dla poszczególnych języków** — kontrolują ton, formalność, formy liczby mnogiej i typografię
+- **Pamięcią tłumaczeniową** — pomija niezmienione ciągi znaków, obniżając koszty wywołań API
+- **Deterministyczną walidacją** — wychwytuje brakujące lub nadmiarowe klucze, dryf chronionej struktury, niezgodności z glosariuszem oraz błędy form liczby mnogiej lub formatu ICU przed wdrożeniem
+
## Instalacja
Zainstaluj z npm:
@@ -40,13 +42,13 @@ Zainstaluj z npm:
npm install -g internationalizer
```
-Lub uruchom bez globalnej instalacji:
+Lub uruchom bez instalacji globalnej:
```bash
npx internationalizer --help
```
-Pakiet npm instaluje pasujący, wstępnie skompilowany plik binarny z npm poprzez opcjonalne zależności specyficzne dla platformy.
+Pakiet npm instaluje odpowiedni, prekompilowany plik binarny z npm za pośrednictwem opcjonalnych zależności specyficznych dla danej platformy.
Zainstaluj za pomocą Go:
@@ -54,7 +56,7 @@ Zainstaluj za pomocą Go:
go install github.com/Tom-R-Main/Internationalizer/cmd/internationalizer@latest
```
-Lub zbuduj ze źródeł:
+Lub skompiluj ze źródeł:
```bash
git clone https://github.com/Tom-R-Main/Internationalizer.git
@@ -62,13 +64,15 @@ cd Internationalizer
go build -o internationalizer ./cmd/internationalizer
```
+
## Pakiety npm
- Tagi Git i wersje pakietów npm muszą być zgodne, na przykład `v0.1.0` i `0.1.0`
- Główny pakiet `internationalizer` zależy od pakietów platformowych, takich jak `internationalizer-darwin-arm64`
-- Obsługiwane cele npm: macOS arm64/x64, Linux arm64/x64, Windows x64
-- Publikowanie w CI wymaga sekretu GitHub o nazwie `NPM_TOKEN`
+- Obsługiwane platformy docelowe npm: macOS arm64/x64, Linux arm64/x64, Windows x64
+- Publikowanie w ramach CI wymaga sekretu GitHub o nazwie `NPM_TOKEN`
+
## Szybki start
1. Utwórz plik konfiguracyjny w katalogu głównym projektu:
@@ -77,7 +81,11 @@ go build -o internationalizer ./cmd/internationalizer
# .internationalizer.yml
source_locale: en
target_locales: [fr, de, es, ja]
-source_path: locales/en.json
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
llm:
provider: gemini
@@ -103,36 +111,65 @@ internationalizer translate --dry-run
internationalizer translate
```
-5. Zweryfikuj wszystkie języki:
+5. Zweryfikuj wszystkie wersje językowe:
```bash
internationalizer validate
```
+
## Polecenia
### `translate`
-Znajdź brakujące klucze i przetłumacz je za pomocą LLM.
+Znajdź brakujące lub nieaktualne klucze i przetłumacz je za pomocą LLM.
```bash
-internationalizer translate # przetłumacz wszystkie języki
-internationalizer translate -l fr # przetłumacz tylko język francuski
-internationalizer translate --dry-run # podgląd bez wywołań API
-internationalizer translate --batch-size 20 # mniejsze partie
-internationalizer translate --concurrency 2 # mniej równoległych wywołań
+internationalizer translate # translate all locales
+internationalizer translate -l fr # translate French only
+internationalizer translate --dry-run # preview without API calls
+internationalizer translate --adopt-existing # baseline existing translations without API calls
+internationalizer translate --refresh-policy # refresh prompt/style/model-stale entries
+internationalizer translate --batch-size 20 # smaller batches
+internationalizer translate --concurrency 2 # fewer parallel calls
```
+Stan tłumaczenia niezależnie raportuje stany: brakujące, nieaktualne względem źródła (source-stale), nieaktualne względem zasad (policy-stale), aktualne oraz zmodyfikowane ręcznie, dzięki czemu ręczna edycja nie może zamaskować zmiany w źródle lub zasadach. Wartości nieaktualne względem zasad są raportowane, lecz zostają przetłumaczone ponownie wyłącznie przy użyciu opcji `--refresh-policy`. Ręcznie zmodyfikowane wartości nigdy nie są nadpisywane automatycznie. Użyj opcji `--adopt-existing` przy wprowadzaniu manifestu do przejrzanych tłumaczeń lub przy wyraźnym zaakceptowaniu przejrzanej edycji ręcznej jako nowego punktu bazowego.
+
### `validate`
-Sprawdź wszystkie pliki językowe pod kątem brakujących kluczy, dodatkowych kluczy i niezgodności interpolacji.
+Sprawdź wszystkie pliki językowe względem ich pakietów źródłowych. Domyślna walidacja sprawdza pokrycie strukturalne (procent obecnych wymaganych kluczy docelowych), raportuje nadmiarowe klucze jako ostrzeżenia i kończy się niepowodzeniem w przypadku brakujących kluczy, niezgodności interpolacji lub nieprawidłowej struktury ICU MessageFormat.
```bash
-internationalizer validate # czytelne dla człowieka dane wyjściowe
-internationalizer validate --json # format JSON czytelny dla maszyn
-internationalizer validate -q # tylko kod wyjścia
+internationalizer validate # human-readable output
+internationalizer validate --json # machine-readable JSON
+internationalizer validate -q # exit code only
+internationalizer validate --strict # enforce translation quality rules
+internationalizer validate --require-state # require current manifest provenance
```
+Opcja `--strict` raportuje również pokrycie przetłumaczenia. Wartość językowa identyczna ze źródłem jest uznawana za nieprzetłumaczoną, chyba że glosariusz zawiera jednoznaczny wpis o identycznym źródle i celu dla całej wartości; parametr `ignore_case` jest uwzględniany, ale termin z glosariusza osadzony w dłuższej wartości nie stanowi wyjątku. Tryb ścisły kończy się niepowodzeniem w przypadku nadmiarowych kluczy, wartości identycznych ze źródłem, zmienionej struktury interpolacji/HTML/kodu/łączy Markdown, naruszeń glosariusza oraz brakujących skonfigurowanych form liczby mnogiej.
+
+Opcja `--require-state` weryfikuje każdy plik docelowy względem `.internationalizer.lock`. Zgłasza błąd, gdy klucz nie jest śledzony lub gdy jego zapisany skrót źródła, zasad tłumaczenia bądź wartości docelowej jest nieaktualny. Opcję tę można łączyć z `--strict`.
+
+Raporty czytelne dla człowieka oraz JSON korzystają ze stabilnych kodów wyników:
+
+| Kod | Znaczenie |
+| --- | --- |
+| `missing_key` / `extra_key` | Zestawy kluczy źródłowych i docelowych różnią się |
+| `blank_translation` | Niepusta wartość źródłowa ma pustą wartość docelową w trybie ścisłym |
+| `source_identical` | Wartość językowa w trybie ścisłym pozostaje nieprzetłumaczona |
+| `protected_structure_mismatch` | Zmiana struktury interpolacji, HTML, kodu lub łącza |
+| `glossary_violation` | Nie znaleziono zatwierdzonego terminu docelowego ani jego wariantu |
+| `plural_form_missing` | Brak skonfigurowanej formy liczby mnogiej dla danego języka |
+| `icu_message_syntax` | Niepoprawna składnia komunikatu ICU w źródle lub pliku docelowym |
+| `icu_argument_mismatch` | Różnice w nazwach, typach lub stylach formatowania argumentów ICU |
+| `icu_selector_mismatch` | Selektory różnią się lub kategoria liczby mnogiej jest nieprawidłowa dla docelowego języka |
+| `untracked` | Brak wpisu w manifeście dla elementu docelowego |
+| `source_stale` | Zawartość źródłowa zmieniła się po zarejestrowaniu tłumaczenia |
+| `policy_stale` | Wygenerowany prompt lub ustawienia modelu uległy zmianie |
+| `target_modified` | Zawartość docelowa różni się od wpisu w manifeście |
+
### `detect`
Automatycznie wykryj framework i18n i zasugeruj konfigurację.
@@ -145,7 +182,7 @@ Obsługuje: react-i18next, next-intl, vue-i18n, czysty JSON, dokumenty Markdown.
### `glossary`
-Zarządzaj terminami w glosariuszu dla poszczególnych języków, które są wymuszane podczas tłumaczenia.
+Zarządzaj terminami glosariusza dla poszczególnych języków, które są egzekwowane podczas tłumaczenia.
```bash
internationalizer glossary list --locale fr
@@ -158,163 +195,232 @@ internationalizer glossary remove --locale fr --source "Dashboard"
Zarządzaj pamięcią tłumaczeniową (pamięć podręczna JSONL wcześniej przetłumaczonych ciągów znaków).
```bash
-internationalizer tm stats # pokaż liczbę rekordów
-internationalizer tm export # zrzuć jako JSON
-internationalizer tm clear --force # usuń wszystkie rekordy
+internationalizer tm stats # show record counts
+internationalizer tm export # dump as JSON
+internationalizer tm clear --force # delete all records
```
+
## Dokumentacja konfiguracji
```yaml
# .internationalizer.yml
-# Język źródłowy (domyślnie: en)
+# Source language (default: en)
source_locale: en
-# Języki docelowe tłumaczenia (wymagane)
-target_locales: [fr, de, es, ja, zh-CN, ar]
-
-# Ścieżka do źródłowego pliku językowego (wymagane)
-source_path: locales/en.json
-
-# Ustawienia dostawcy LLM
+# Languages to translate into (required)
+target_locales: [fr, de, es, ja, yue, zh-CN, zh-TW, ar]
+
+# One or more source-to-target mappings (required).
+# {locale} is replaced with each configured target locale.
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
+ - id: docs
+ source: README.md
+ target: docs/i18n/{locale}.md
+ format: markdown
+
+# Backward compatibility: source_path still maps targets to sibling files
+# such as locales/fr.json. Prefer bundles for new projects.
+# source_path: locales/en.json
+
+# LLM provider settings
llm:
- # Dostawca: "anthropic", "openai", "gemini" lub "openrouter" (domyślnie: gemini)
+ # Provider: "anthropic", "openai", "gemini", or "openrouter" (default: gemini)
provider: gemini
- # Domyślne nazwy modeli według dostawcy:
+ # Model name defaults by provider:
# anthropic: claude-opus-5
- # openai: gpt-5.6-luna
+ # openai: gpt-5.6-luna (reasoning effort defaults to max)
# gemini: gemini-3.8-flash
# openrouter: deepseek/deepseek-v4-pro-0813
model: gemini-3.8-flash
- # Zmienna środowiskowa zawierająca klucz API
+ # Environment variable containing the API key
api_key_env: GOOGLE_AI_STUDIO_API_KEY
- # Bazowy adres URL dla punktów końcowych zgodnych z OpenAI (opcjonalnie)
+ # Base URL for OpenAI-compatible endpoints (optional)
# base_url: https://api.openai.com
-# Liczba kluczy na wywołanie LLM (domyślnie: 40)
+ # OpenAI GPT-5-series Responses API reasoning effort
+ # (default: max for the OpenAI provider)
+ reasoning_effort: max
+
+ # Optional LLM settings for individual target locales. An override using the
+ # global provider inherits unspecified global settings. A different provider
+ # uses that provider's defaults for unspecified settings.
+ locale_overrides:
+ yue:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-CN:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-TW:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+
+# Keys per LLM call (default: 40)
batch_size: 40
-# Równoległe wywołania LLM (domyślnie: 4)
+# Parallel LLM calls (default: 4)
concurrency: 4
-# Katalog zawierający pliki Markdown z przewodnikami po stylu dla poszczególnych języków (domyślnie: style-guides)
+# Directory containing per-locale style guide Markdown files (default: style-guides)
style_guides_dir: style-guides
-# Katalog zawierający pliki JSON z glosariuszami dla poszczególnych języków (domyślnie: glossary)
+# Directory containing per-locale glossary JSON files (default: glossary)
glossary_dir: glossary
-# Ścieżka do pliku pamięci tłumaczeniowej (domyślnie: .internationalizer/tm.jsonl)
+# Path to translation memory file (default: .internationalizer/tm.jsonl)
tm_path: .internationalizer/tm.jsonl
+
+# Versioned source, policy, target, and provenance state
+# (default: .internationalizer.lock; commit this file)
+manifest_path: .internationalizer.lock
+
+# Optional translation and strict-validation rules
+validation:
+ plural_style: i18next-v4 # generate and validate target-locale plural forms
```
+Identyfikatory ustawień regionalnych muszą być prawidłowymi znacznikami BCP 47, takimi jak `fr`, `pt-BR` lub `sr-Latn-RS`. Równoważne kanonicznie docelowe ustawienia regionalne są odrzucane jako duplikaty, a specyficzne dla ustawień regionalnych nadpisania dostawców są dopasowywane według pisowni kanonicznej. W powyższym przykładzie języki docelowe bez nadpisania — w tym japoński — dziedziczą globalną konfigurację Gemini.
+
+Wartości ICU MessageFormat są analizowane pod kątem struktury. Obsługiwane są proste argumenty, `select`, `plural`, `selectordinal`, `number`, `date` oraz `time`, w tym zagnieżdżone komunikaty, przesunięcia liczby mnogiej (plural offsets), selektory dokładnych liczb oraz `#`. Walidacja weryfikuje składnię, typy argumentów i style formatowania, przesunięcia liczby mnogiej, zgodność gałęzi instrukcji select oraz kategorie liczb mnogich CLDR dla docelowego języka. Wyniki od dostawcy, które naruszają te niezmienniki, są odrzucane przed zapisaniem pliku językowego lub rekordu w pamięci tłumaczeniowej.
+
+W przypadku `i18next-v4` rozpoznane źródłowe rodziny liczb mnogich są rozwijane podczas tłumaczenia do kategorii CLDR docelowego języka. Kategoria występująca wyłącznie w języku docelowym używa wartości `_other` rodziny źródłowej jako szablonu tłumaczenia. Walidacja ścisła wymaga tych kategorii docelowych; kategorie występujące wyłącznie w źródle są opcjonalne dla języków docelowych, które ich nie używają.
+
+
## Przewodniki po stylu
-Przewodniki po stylu to pliki Markdown, które są wstrzykiwane do promptu tłumaczenia LLM. Kontrolują one ton, formalność, typografię i inne konwencje specyficzne dla danego języka.
+Przewodniki po stylu to pliki Markdown wstrzykiwane do promptu tłumaczeniowego LLM. Sterują one tonem, formalnością, typografią i innymi konwencjami specyficznymi dla danego języka.
```
style-guides/
- _conventions.md # wspólne reguły dla wszystkich języków
- fr.md # reguły specyficzne dla języka francuskiego
- ja.md # reguły specyficzne dla języka japońskiego
- ar.md # reguły specyficzne dla języka arabskiego
+ _conventions.md # shared rules for all languages
+ fr.md # French-specific rules
+ ja.md # Japanese-specific rules
+ ar.md # Arabic-specific rules
```
### Wspólne konwencje (`_conventions.md`)
-Zdefiniuj reguły, które mają zastosowanie do wszystkich języków: składnię interpolacji, zachowywanie tagów HTML, konwencje typów ciągów znaków (przyciski vs etykiety vs błędy) itp.
+Definiują reguły mające zastosowanie do wszystkich języków: składnię interpolacji, zachowywanie znaczników HTML, konwencje typów ciągów znaków (przyciski a etykiety a błędy) itp.
### Przewodniki dla poszczególnych języków (`{locale}.md`)
-Zdefiniuj reguły specyficzne dla danego języka: rejestr formalności (np. ty vs Pan/Pani), interpunkcję (cudzysłowy francuskie, odwrócone znaki zapytania), formy liczby mnogiej, formatowanie dat/liczb oraz glosariusz terminologiczny.
+Definiują reguły specyficzne dla danego języka: rejestr formalności (np. ty a Pan/Pani), interpunkcję (cudzysłowy francuskie, odwrócone znaki zapytania), formy liczby mnogiej, formatowanie dat i liczb oraz glosariusz terminologiczny.
-Zobacz [`examples/react-app/style-guides/`](examples/react-app/style-guides/), aby zapoznać się z działającym przykładem.
+Przewodniki po stylu stanowią trwałe dane wejściowe zasad, a nie generowane dane wyjściowe. Internationalizer odczytuje je, lecz nigdy ich nie modyfikuje. Ich zawartość jest haszowana niezależnie od glosariusza i kontraktu promptu, dzięki czemu zmiana kodu aplikacji nie powoduje dezaktualizacji tłumaczenia. Edycja przewodnika celowo oznacza dany język do przeglądu zasad; zmiana wewnętrznego sformułowania promptu tego nie powoduje, chyba że zmieni się również wersja kontraktu promptu.
+Zobacz [`examples/react-app/style-guides/`](../../examples/react-app/style-guides/), aby zapoznać się z działającym przykładem.
+
+
## Format glosariusza
-Pliki glosariusza to tablice JSON przechowywane w `{glossary_dir}/{locale}.json`:
+Pliki glosariusza to tablice JSON zapisane w `{glossary_dir}/{locale}.json`:
```json
[
{
"source": "Dashboard",
"target": "Tableau de bord",
+ "variants": ["Panneau de contrôle"],
+ "enforcement": "error",
"ignore_case": false,
"whole_word": true
}
]
```
-Terminy są wstrzykiwane do promptu LLM jako tabela terminologiczna, co zapewnia spójne tłumaczenie kluczowych terminów w całej aplikacji.
+Pole `variants` zawiera listę innych zatwierdzonych form docelowych. Pole `enforcement` może przyjmować wartość `error`, `warning` lub zostać pominięte (wtedy domyślnym zachowaniem jest błąd). Terminy są wstrzykiwane do promptu LLM jako tabela terminologiczna, co zapewnia spójne tłumaczenie w całej aplikacji. Dokładny wpis, taki jak `{"source":"API","target":"API"}`, zwalnia również całą wartość identyczną ze źródłem z ustaleń o nieprzetłumaczonych wartościach w trybie ścisłym; nie zwalnia to jednak dłuższej wartości, która jedynie zawiera `API`.
+
## Pamięć tłumaczeniowa
-Pamięć tłumaczeniowa jest przechowywana jako plik JSONL (jeden rekord JSON na linię). Każdy rekord zawiera:
+Pamięć tłumaczeniowa jest przechowywana jako plik JSONL (jeden rekord JSON na wiersz). Każdy rekord zawiera:
-- Źródłowy klucz i wartość
-- Przetłumaczoną wartość
-- Skrót SHA-256 wartości źródłowej
+- Pakiet, klucz, wartość źródłową, przetłumaczoną wartość oraz kanoniczne docelowe ustawienia regionalne
+- Skróty źródła, przewodnika po stylu, glosariusza, kontraktu promptu oraz połączonych zasad
+- Nazwę dostawcy i model, który wygenerował tłumaczenie
- Znacznik czasu
-Podczas kolejnych uruchomień niezmienione ciągi znaków są serwowane z pamięci podręcznej TM bez wywoływania LLM, co oszczędza czas i koszty API. Plik TM jest przyjazny dla systemu Git i może być commitowany wraz z plikami językowymi.
+Podczas kolejnych uruchomień ciągi znaków o identycznych skrótach źródła i zasad są pobierane z pamięci podręcznej bez wywoływania LLM. Domyślna ścieżka znajduje się w ignorowanym katalogu `.internationalizer/`, dzięki czemu pozostaje lokalną pamięcią podręczną. Ustaw `tm_path` na śledzoną lokalizację, jeśli projekt celowo współdzieli pamięć tłumaczeniową. Przeznaczony do przeglądu manifest `.internationalizer.lock` jest wersjonowany niezależnie.
+
## Obsługiwane formaty
| Format | Rozszerzenia | Tryb |
|--------|-----------|------|
-| JSON | `.json` | Klucz-wartość (zagnieżdżone, spłaszczone z notacją kropkową) |
+| JSON | `.json` | Klucz-wartość (zagnieżdżone, spłaszczone notacją kropkową) |
| YAML | `.yml`, `.yaml` | Klucz-wartość (zachowuje komentarze i kolejność) |
-| Markdown | `.md`, `.mdx` | Tłumaczenie całego dokumentu |
+| Markdown | `.md`, `.mdx` | Preambuła i sekcje poziomu H2 |
+
+Docelowe pliki Markdown zawierają niewidoczne komentarze `internationalizer:unit` przed sekcjami H2. Te stabilne znaczniki pozwalają narzędziu Internationalizer dodawać, przenosić lub modyfikować pojedynczą sekcję źródłową bez ponownego tłumaczenia niepowiązanych sekcji. Istniejące dokumenty bez znaczników otrzymają je przy kolejnej udanej aktualizacji.
+
## Wykrywanie typu projektu
Polecenie `internationalizer detect` identyfikuje Twoją konfigurację i18n, sprawdzając:
-- Zależności w `package.json` dla react-i18next, next-intl lub vue-i18n
-- Struktury katalogów pasujące do popularnych wzorców językowych
+- Zależności w `package.json` pod kątem react-i18next, next-intl lub vue-i18n
+- Struktury katalogów pasujące do popularnych wzorców ustawień regionalnych
- Rozszerzenia plików i konwencje nazewnictwa
+
## Architektura
```
-cmd/internationalizer/ Punkt wejścia CLI i definicje poleceń
+cmd/internationalizer/ CLI entry point and command definitions
internal/
- config/ Ładowanie konfiguracji YAML z wartościami domyślnymi
- detect/ Automatyczne wykrywanie typu projektu
- formats/ Parsery formatów (JSON, YAML, Markdown)
- glossary/ Zarządzanie glosariuszem dla poszczególnych języków
- llm/ Interfejs dostawcy LLM + implementacje
- anthropic.go Backend Anthropic Claude
- openai.go Backend OpenAI / kompatybilny
- gemini.go Backend Google Gemini przez AI Studio
- OpenRouter używa openai.go z niestandardowym base_url
- styleguide/ Moduł ładujący przewodnik po stylu
- tm/ Pamięć tłumaczeniowa JSONL
- translate/ Orkiestrator tłumaczeń
- validate/ Weryfikacja i porównywanie języków
+ config/ YAML config loading with defaults
+ detect/ Project type auto-detection
+ formats/ Format parsers (JSON, YAML, Markdown)
+ glossary/ Per-locale glossary management
+ llm/ LLM provider interface + implementations
+ anthropic.go Anthropic Claude backend
+ openai.go OpenAI / compatible backend
+ gemini.go Google Gemini via AI Studio backend
+ OpenRouter uses openai.go with custom base_url
+ locale/ BCP 47 identity and CLDR plural categories
+ message/ ICU MessageFormat parser and structural comparison
+ policy/ Stable translation-policy hashing
+ state/ Versioned translation manifest
+ styleguide/ Style guide loader
+ tm/ JSONL translation memory
+ translate/ Translation orchestrator
+ validate/ Locale validation and diffing
```
+
## Porównanie z alternatywami
-| Funkcja | Internationalizer | i18next | Crowdin | Ogólne LLM |
+| Funkcja | Internationalizer | i18next | Crowdin | Ogólny LLM |
|---------|------------------|---------|---------|-------------|
| Tłumaczenie oparte na LLM | Tak | Nie | Częściowo | Tak |
| Przewodniki po stylu dla języków | Tak | Nie | Nie | Nie |
| Wymuszanie glosariusza | Tak | Nie | Tak | Nie |
| Pamięć tłumaczeniowa | Tak | Nie | Tak | Nie |
-| CLI / wykonanie lokalne | Tak | N/D | Nie | Ręcznie |
+| CLI / uruchamianie lokalne | Tak | N/D | Nie | Ręcznie |
| Pliki przyjazne dla Git | Tak | Tak | Częściowo | Ręcznie |
| Brak zależności od SaaS | Tak | Tak | Nie | Różnie |
-| Open source (AGPL-3.0) | Tak | Tak | Nie | Różnie |
+| Otwarte oprogramowanie (AGPL-3.0) | Tak | Tak | Nie | Różnie |
+
## Licencja
-[AGPL-3.0](LICENSE)
+[AGPL-3.0](../../LICENSE)
-## Współtworzenie
+Zobacz [THIRD_PARTY_NOTICES.md](../../THIRD_PARTY_NOTICES.md), aby zapoznać się z informacjami o zależnościach.
-Zobacz [CONTRIBUTING.md](CONTRIBUTING.md), aby zapoznać się z konfiguracją środowiska programistycznego i wytycznymi. Wszystkie wkłady wymagają zatwierdzenia DCO.
+
+## Współtworzenie
+Zobacz [CONTRIBUTING.md](../../CONTRIBUTING.md), aby zapoznać się z konfiguracją środowiska programistycznego i wytycznymi. Wszystkie wkłady wymagają zatwierdzenia DCO.
diff --git a/docs/i18n/pt-BR.md b/docs/i18n/pt-BR.md
index 4d5e240..31b5594 100644
--- a/docs/i18n/pt-BR.md
+++ b/docs/i18n/pt-BR.md
@@ -6,29 +6,35 @@
# Internationalizer
-Pipeline de internacionalização nativo de IA para projetos de software. Traduza, valide e gerencie arquivos i18n usando LLMs.
+Pipeline de internacionalização nativo de IA para projetos de software. Traduza, valide e gerencie arquivos de i18n usando LLMs.
[](https://github.com/Tom-R-Main/Internationalizer/actions/workflows/ci.yml)
-[](LICENSE)
+[](../../LICENSE)
+
+العربية · বাংলা · Čeština · Dansk · Deutsch · Ελληνικά · Español · Suomi · Français · עברית · हिन्दी · Indonesia · Italiano · 日本語 · 한국어 · Bahasa Melayu
Nederlands · ਪੰਜਾਬੀ · Polski · Português · Română · Русский · Svenska · తెలుగు · ไทย · Türkçe · Українська · Tiếng Việt · 粵語 · 简体中文 · 繁體中文
+
+
+---
+
## Por que o Internationalizer?
-A maioria das ferramentas de i18n são bibliotecas de tempo de execução (i18next, react-intl) ou plataformas SaaS de gerenciamento de chaves (Crowdin, Lokalise). Nenhuma delas resolve bem o problema real de tradução:
+A maioria das ferramentas de i18n é composta por bibliotecas de runtime (i18next, react-intl) ou plataformas SaaS de gerenciamento de chaves (Crowdin, Lokalise). Nenhuma delas resolve bem o verdadeiro problema de tradução:
-- **Tradução manual** não escala além de alguns idiomas
-- **APIs de tradução automática** (Google Translate, DeepL) ignoram sua terminologia, tom e convenções de interface de usuário (UI)
-- **Tradução genérica por LLM** funciona melhor, mas sem glossários e guias de estilo, você obtém resultados inconsistentes
+- **A tradução manual** não escala além de alguns idiomas
+- **APIs de tradução automática** (Google Translate, DeepL) ignoram sua terminologia, seu tom e as convenções de UI
+- **A tradução genérica por LLM** funciona melhor, mas sem glossários e guias de estilo você obtém resultados inconsistentes
-O Internationalizer é diferente. É um **pipeline de CLI** que combina tradução por LLM com:
+O Internationalizer é diferente. É um **pipeline de CLI** que combina a tradução por LLM com:
-- **Glossários por idioma** — impõe terminologia consistente em todo o seu aplicativo
-- **Guias de estilo por idioma** — controla o tom, a formalidade, a pluralização e a tipografia
+- **Glossários por idioma** — garantem terminologia consistente em todo o seu aplicativo
+- **Guias de estilo por idioma** — controlam o tom, a formalidade, a pluralização e a tipografia
- **Memória de tradução** — ignora strings inalteradas, economizando dinheiro em chamadas de API
-- **Validação de chaves** — detecta traduções ausentes e incompatibilidades de interpolação antes do lançamento
-
+- **Validação determinística** — detecta chaves ausentes ou excedentes, desvios de estrutura protegida, problemas de glossário e erros de plural ou ICU antes do lançamento
+
## Instalação
-Instale a partir do npm:
+Instale via npm:
```bash
npm install -g internationalizer
@@ -55,15 +61,15 @@ git clone https://github.com/Tom-R-Main/Internationalizer.git
cd Internationalizer
go build -o internationalizer ./cmd/internationalizer
```
-
+
## Pacotes npm
-- As tags do Git e as versões do pacote npm devem corresponder, por exemplo, `v0.1.0` e `0.1.0`
+- As tags do Git e as versões do pacote npm devem corresponder; por exemplo, `v0.1.0` e `0.1.0`
- O pacote raiz `internationalizer` depende de pacotes de plataforma, como `internationalizer-darwin-arm64`
-- Alvos npm suportados: macOS arm64/x64, Linux arm64/x64, Windows x64
-- A publicação via CI requer um secret do GitHub chamado `NPM_TOKEN`
-
-## Início Rápido
+- Destinos npm compatíveis: macOS arm64/x64, Linux arm64/x64, Windows x64
+- A publicação via CI requer um secret do GitHub com o nome `NPM_TOKEN`
+
+## Início rápido
1. Crie um arquivo de configuração na raiz do seu projeto:
@@ -71,7 +77,11 @@ go build -o internationalizer ./cmd/internationalizer
# .internationalizer.yml
source_locale: en
target_locales: [fr, de, es, ja]
-source_path: locales/en.json
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
llm:
provider: gemini
@@ -85,7 +95,7 @@ llm:
export GOOGLE_AI_STUDIO_API_KEY=your-ai-studio-key
```
-3. Visualize o que será traduzido:
+3. Faça uma prévia do que será traduzido:
```bash
internationalizer translate --dry-run
@@ -102,40 +112,68 @@ internationalizer translate
```bash
internationalizer validate
```
-
+
## Comandos
### `translate`
-Encontre chaves ausentes e traduza-as via um LLM.
+Encontre chaves ausentes ou desatualizadas e traduza-as por meio de um LLM.
```bash
-internationalizer translate # traduzir todos os locales
-internationalizer translate -l fr # traduzir apenas para francês
-internationalizer translate --dry-run # visualizar sem chamadas de API
-internationalizer translate --batch-size 20 # lotes menores
-internationalizer translate --concurrency 2 # menos chamadas paralelas
+internationalizer translate # translate all locales
+internationalizer translate -l fr # translate French only
+internationalizer translate --dry-run # preview without API calls
+internationalizer translate --adopt-existing # baseline existing translations without API calls
+internationalizer translate --refresh-policy # refresh prompt/style/model-stale entries
+internationalizer translate --batch-size 20 # smaller batches
+internationalizer translate --concurrency 2 # fewer parallel calls
```
+O estado de tradução relata de forma independente as condições de ausente, com origem desatualizada (source-stale), com política desatualizada (policy-stale), atual e editado manualmente; assim, uma edição manual não consegue ocultar uma alteração na origem ou na política. Valores com política desatualizada são relatados, mas só são retraduzidos com `--refresh-policy`. Valores editados manualmente nunca são substituídos de forma automática. Use `--adopt-existing` ao introduzir o manifesto em traduções já revisadas ou ao aceitar explicitamente uma edição manual revisada como a nova linha de base.
+
### `validate`
-Verifique todos os arquivos de locale em busca de chaves ausentes, chaves extras e incompatibilidades de interpolação.
+Verifique todos os arquivos de locale em relação aos respectivos bundles de origem. A validação padrão verifica a cobertura estrutural (a porcentagem de chaves de destino necessárias presentes), relata chaves extras como avisos e falha em caso de chaves ausentes, incompatibilidades de interpolação ou estrutura de ICU MessageFormat inválida.
```bash
-internationalizer validate # saída legível por humanos
-internationalizer validate --json # JSON legível por máquina
-internationalizer validate -q # apenas código de saída
+internationalizer validate # human-readable output
+internationalizer validate --json # machine-readable JSON
+internationalizer validate -q # exit code only
+internationalizer validate --strict # enforce translation quality rules
+internationalizer validate --require-state # require current manifest provenance
```
+O parâmetro `--strict` também relata a cobertura traduzida. Um valor linguístico idêntico à sua origem é considerado não traduzido, a menos que o glossário contenha explicitamente uma entrada idêntica de mesma origem e mesmo destino para o valor completo; `ignore_case` é respeitado, mas um termo de glossário inserido em um valor mais longo não constitui isenção. O modo estrito falha em caso de chaves extras, valores idênticos à origem, alterações na estrutura de interpolação/HTML/código/links de Markdown, violações de glossário e formas plurais configuradas.
+
+O parâmetro `--require-state` verifica cada destino em relação ao arquivo `.internationalizer.lock`. Ele falha quando uma chave não é rastreada ou quando a origem gravada, a política de tradução ou o hash de destino estão desatualizados. Ele pode ser combinado com `--strict`.
+
+Os relatórios legíveis por humanos e em JSON usam códigos de constatação estáveis:
+
+| Código | Significado |
+| --- | --- |
+| `missing_key` / `extra_key` | Os conjuntos de chaves de origem e de destino diferem |
+| `blank_translation` | Uma origem não vazia tem um destino vazio no modo estrito |
+| `source_identical` | Um valor linguístico no modo estrito permanece não traduzido |
+| `protected_structure_mismatch` | A estrutura de interpolação, HTML, código ou links foi alterada |
+| `glossary_violation` | Nenhum termo ou variante de destino aprovado foi encontrado |
+| `plural_form_missing` | Uma forma plural configurada para o locale está ausente |
+| `icu_message_syntax` | Uma mensagem ICU de origem ou de destino está malformada |
+| `icu_argument_mismatch` | Nomes, tipos ou estilos de formatador dos argumentos ICU diferem |
+| `icu_selector_mismatch` | Os seletores diferem ou uma categoria de plural é inválida para o locale de destino |
+| `untracked` | Não há registro no manifesto para o destino |
+| `source_stale` | O conteúdo de origem foi alterado após a tradução gravada |
+| `policy_stale` | As configurações do modelo ou o prompt gerado foram alterados |
+| `target_modified` | O conteúdo de destino difere do registro no manifesto |
+
### `detect`
-Detecte automaticamente o framework i18n e sugira uma configuração.
+Detecte automaticamente o framework de i18n e sugira uma configuração.
```bash
internationalizer detect
```
-Suporta: react-i18next, next-intl, vue-i18n, JSON puro, documentos markdown.
+Compatível com: react-i18next, next-intl, vue-i18n, JSON puro, documentos Markdown.
### `glossary`
@@ -152,82 +190,132 @@ internationalizer glossary remove --locale fr --source "Dashboard"
Gerencie a memória de tradução (cache JSONL de strings traduzidas anteriormente).
```bash
-internationalizer tm stats # mostrar contagem de registros
-internationalizer tm export # exportar como JSON
-internationalizer tm clear --force # excluir todos os registros
+internationalizer tm stats # show record counts
+internationalizer tm export # dump as JSON
+internationalizer tm clear --force # delete all records
```
-
-## Referência de Configuração
+
+## Referência de configuração
```yaml
# .internationalizer.yml
-# Idioma de origem (padrão: en)
+# Source language (default: en)
source_locale: en
-# Idiomas para os quais traduzir (obrigatório)
-target_locales: [fr, de, es, ja, zh-CN, ar]
-
-# Caminho para o arquivo de locale de origem (obrigatório)
-source_path: locales/en.json
-
-# Configurações do provedor de LLM
+# Languages to translate into (required)
+target_locales: [fr, de, es, ja, yue, zh-CN, zh-TW, ar]
+
+# One or more source-to-target mappings (required).
+# {locale} is replaced with each configured target locale.
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
+ - id: docs
+ source: README.md
+ target: docs/i18n/{locale}.md
+ format: markdown
+
+# Backward compatibility: source_path still maps targets to sibling files
+# such as locales/fr.json. Prefer bundles for new projects.
+# source_path: locales/en.json
+
+# LLM provider settings
llm:
- # Provedor: "anthropic", "openai", "gemini" ou "openrouter" (padrão: gemini)
+ # Provider: "anthropic", "openai", "gemini", or "openrouter" (default: gemini)
provider: gemini
- # Padrões de nome de modelo por provedor:
+ # Model name defaults by provider:
# anthropic: claude-opus-5
- # openai: gpt-5.6-luna
+ # openai: gpt-5.6-luna (reasoning effort defaults to max)
# gemini: gemini-3.8-flash
# openrouter: deepseek/deepseek-v4-pro-0813
model: gemini-3.8-flash
- # Variável de ambiente contendo a chave de API
+ # Environment variable containing the API key
api_key_env: GOOGLE_AI_STUDIO_API_KEY
- # URL base para endpoints compatíveis com OpenAI (opcional)
+ # Base URL for OpenAI-compatible endpoints (optional)
# base_url: https://api.openai.com
-# Chaves por chamada de LLM (padrão: 40)
+ # OpenAI GPT-5-series Responses API reasoning effort
+ # (default: max for the OpenAI provider)
+ reasoning_effort: max
+
+ # Optional LLM settings for individual target locales. An override using the
+ # global provider inherits unspecified global settings. A different provider
+ # uses that provider's defaults for unspecified settings.
+ locale_overrides:
+ yue:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-CN:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-TW:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+
+# Keys per LLM call (default: 40)
batch_size: 40
-# Chamadas paralelas de LLM (padrão: 4)
+# Parallel LLM calls (default: 4)
concurrency: 4
-# Diretório contendo arquivos Markdown de guia de estilo por locale (padrão: style-guides)
+# Directory containing per-locale style guide Markdown files (default: style-guides)
style_guides_dir: style-guides
-# Diretório contendo arquivos JSON de glossário por locale (padrão: glossary)
+# Directory containing per-locale glossary JSON files (default: glossary)
glossary_dir: glossary
-# Caminho para o arquivo de memória de tradução (padrão: .internationalizer/tm.jsonl)
+# Path to translation memory file (default: .internationalizer/tm.jsonl)
tm_path: .internationalizer/tm.jsonl
+
+# Versioned source, policy, target, and provenance state
+# (default: .internationalizer.lock; commit this file)
+manifest_path: .internationalizer.lock
+
+# Optional translation and strict-validation rules
+validation:
+ plural_style: i18next-v4 # generate and validate target-locale plural forms
```
-## Guias de Estilo
+Os identificadores de locale devem ser tags BCP 47 válidas, como `fr`, `pt-BR` ou `sr-Latn-RS`. Locales de destino canonicamente equivalentes são rejeitados como duplicatas, e as substituições de provedor específicas por locale correspondem à grafia canonicamente equivalente. No exemplo acima, os locales sem substituição — incluindo o japonês — herdam a configuração global do Gemini.
+
+Valores em formato ICU MessageFormat são analisados estruturalmente. Há suporte para argumentos simples, `select`, `plural`, `selectordinal`, `number`, `date` e `time`, incluindo mensagens aninhadas, deslocamentos de plural (offsets), seletores de número exato e `#`. A validação verifica sintaxe, tipos de argumentos e estilos de formatadores, deslocamentos de plural, identidade de ramificações de select e categorias de plural do CLDR para o locale de destino. A resposta do provedor que violar essas invariantes é rejeitada antes que um arquivo de locale ou registro de memória de tradução seja gravado.
+
+Com `i18next-v4`, famílias de plural reconhecidas na origem são expandidas durante a tradução para as categorias CLDR do locale de destino. Uma categoria exclusiva do destino usa o valor `_other` da família de origem como seu modelo de tradução. A validação estrita exige essas categorias de destino; categorias exclusivas da origem são opcionais para locales de destino que não as utilizam.
+
+## Guias de estilo
-Os guias de estilo são arquivos Markdown que são injetados no prompt de tradução do LLM. Eles controlam o tom, a formalidade, a tipografia e outras convenções específicas do idioma.
+Guias de estilo são arquivos Markdown injetados no prompt de tradução do LLM. Eles controlam o tom, a formalidade, a tipografia e outras convenções específicas do idioma.
```
style-guides/
- _conventions.md # regras compartilhadas para todos os idiomas
- fr.md # regras específicas para francês
- ja.md # regras específicas para japonês
- ar.md # regras específicas para árabe
+ _conventions.md # shared rules for all languages
+ fr.md # French-specific rules
+ ja.md # Japanese-specific rules
+ ar.md # Arabic-specific rules
```
### Convenções compartilhadas (`_conventions.md`)
-Defina regras que se aplicam a todos os idiomas: sintaxe de interpolação, preservação de HTML, convenções de tipo de string (botões vs. rótulos vs. erros), etc.
+Defina regras que se aplicam a todos os idiomas: sintaxe de interpolação, preservação de HTML, convenções de tipos de strings (botões vs. rótulos vs. erros), etc.
### Guias por idioma (`{locale}.md`)
-Defina regras específicas do idioma: registro de formalidade (tu vs. vous), pontuação (aspas, pontos de interrogação invertidos), formas plurais, formatação de data/número e um glossário de terminologia.
+Defina regras específicas do idioma: registro de formalidade (tu vs. vous), pontuação (aspas angulares, pontos de interrogação invertidos), formas plurais, formatação de data/número e um glossário de terminologia.
-Consulte [`examples/react-app/style-guides/`](examples/react-app/style-guides/) para ver um exemplo prático.
+Os guias de estilo são entradas de política permanentes, não saídas geradas. O Internationalizer lê esses guias, mas nunca os reescreve. O conteúdo deles é submetido a hash separadamente do glossário e do contrato de prompt, de modo que uma alteração no código do aplicativo não invalida uma tradução existente. Editar um guia marca intencionalmente esse locale para revisão de política; alterar o texto interno do prompt não faz isso, a menos que a versão do contrato de prompt também mude.
-## Formato do Glossário
+Consulte [`examples/react-app/style-guides/`](../../examples/react-app/style-guides/) para ver um exemplo funcional.
+
+## Formato do glossário
Os arquivos de glossário são arrays JSON armazenados em `{glossary_dir}/{locale}.json`:
@@ -236,79 +324,88 @@ Os arquivos de glossário são arrays JSON armazenados em `{glossary_dir}/{local
{
"source": "Dashboard",
"target": "Tableau de bord",
+ "variants": ["Panneau de contrôle"],
+ "enforcement": "error",
"ignore_case": false,
"whole_word": true
}
]
```
-Os termos são injetados no prompt do LLM como uma tabela de terminologia, garantindo a tradução consistente de termos-chave em todo o seu aplicativo.
-
-## Memória de Tradução
+O campo `variants` lista outras formas de destino aprovadas. O campo `enforcement` pode ser `error`, `warning` ou omitido para o comportamento padrão de erro. Os termos são injetados no prompt do LLM como uma tabela de terminologia, garantindo uma tradução consistente em todo o seu aplicativo. Uma entrada exata como `{"source":"API","target":"API"}` também isenta esse valor idêntico à origem completo de constatações de valor não traduzido no modo estrito; ela não isenta um valor mais longo que apenas contenha `API`.
+
+## Memória de tradução
A memória de tradução é armazenada como um arquivo JSONL (um registro JSON por linha). Cada registro contém:
-- A chave e o valor de origem
-- O valor traduzido
-- Um hash SHA-256 do valor de origem
+- O bundle, a chave, o valor de origem, o valor traduzido e o locale canônico de destino
+- Hashes da origem, do guia de estilo, do glossário, do contrato de prompt e da política combinada
+- O provedor e o modelo que geraram a tradução
- Um carimbo de data/hora (timestamp)
-Em execuções subsequentes, as strings inalteradas são servidas a partir do cache da TM (Memória de Tradução) sem chamar o LLM, economizando tempo e custos de API. O arquivo da TM é amigável ao git e pode ser comitado junto com seus arquivos de locale.
-
-## Formatos Suportados
+Em execuções seguintes, strings com os mesmos hashes de origem e de política são recuperadas do cache sem chamar o LLM. O caminho padrão fica dentro do diretório ignorado `.internationalizer/`, portanto permanece como um cache local. Defina `tm_path` para um local rastreado se o seu projeto compartilha a memória de tradução intencionalmente. O manifesto `.internationalizer.lock` revisável é versionado separadamente.
+
+## Formatos compatíveis
| Formato | Extensões | Modo |
-|--------|-----------|------|
-| JSON | `.json` | Chave-valor (aninhado, achatado por notação de ponto) |
+| --- | --- | --- |
+| JSON | `.json` | Chave-valor (aninhado, nivelado por notação de ponto) |
| YAML | `.yml`, `.yaml` | Chave-valor (preserva comentários e ordenação) |
-| Markdown | `.md`, `.mdx` | Tradução de documento inteiro |
+| Markdown | `.md`, `.mdx` | Preâmbulo e seções de nível H2 |
-## Detecção de Tipo de Projeto
+Os arquivos Markdown de destino contêm comentários invisíveis `internationalizer:unit` antes das seções H2. Esses marcadores estáveis permitem que o Internationalizer adicione, mova ou edite uma seção de origem sem retraduzir seções não relacionadas. Documentos existentes sem marcação recebem marcadores na próxima atualização bem-sucedida.
+
+## Detecção de tipo de projeto
-O `internationalizer detect` identifica sua configuração de i18n verificando:
+O comando `internationalizer detect` identifica sua configuração de i18n verificando:
- Dependências no `package.json` para react-i18next, next-intl ou vue-i18n
-- Estruturas de diretório que correspondem a padrões comuns de locale
+- Estruturas de diretórios correspondentes aos padrões comuns de locale
- Extensões de arquivo e convenções de nomenclatura
-
+
## Arquitetura
```
-cmd/internationalizer/ Ponto de entrada da CLI e definições de comando
+cmd/internationalizer/ CLI entry point and command definitions
internal/
- config/ Carregamento de configuração YAML com padrões
- detect/ Detecção automática de tipo de projeto
- formats/ Analisadores de formato (JSON, YAML, Markdown)
- glossary/ Gerenciamento de glossário por locale
- llm/ Interface do provedor de LLM + implementações
- anthropic.go Backend do Anthropic Claude
- openai.go Backend da OpenAI / compatível
- gemini.go Backend do Google Gemini via AI Studio
- OpenRouter usa openai.go com base_url personalizada
- styleguide/ Carregador de guia de estilo
- tm/ Memória de tradução JSONL
- translate/ Orquestrador de tradução
- validate/ Validação de locale e diffing
+ config/ YAML config loading with defaults
+ detect/ Project type auto-detection
+ formats/ Format parsers (JSON, YAML, Markdown)
+ glossary/ Per-locale glossary management
+ llm/ LLM provider interface + implementations
+ anthropic.go Anthropic Claude backend
+ openai.go OpenAI / compatible backend
+ gemini.go Google Gemini via AI Studio backend
+ OpenRouter uses openai.go with custom base_url
+ locale/ BCP 47 identity and CLDR plural categories
+ message/ ICU MessageFormat parser and structural comparison
+ policy/ Stable translation-policy hashing
+ state/ Versioned translation manifest
+ styleguide/ Style guide loader
+ tm/ JSONL translation memory
+ translate/ Translation orchestrator
+ validate/ Locale validation and diffing
```
+
+## Comparação com alternativas
-## Comparação com Alternativas
-
-| Recurso | Internationalizer | i18next | Crowdin | LLM Genérico |
-|---------|------------------|---------|---------|-------------|
-| Tradução baseada em LLM | Sim | Não | Parcial | Sim |
+| Recurso | Internationalizer | i18next | Crowdin | LLM genérico |
+| --- | --- | --- | --- | --- |
+| Tradução com tecnologia de LLM | Sim | Não | Parcial | Sim |
| Guias de estilo por idioma | Sim | Não | Não | Não |
| Aplicação de glossário | Sim | Não | Sim | Não |
| Memória de tradução | Sim | Não | Sim | Não |
-| CLI / execução local | Sim | N/A | Não | Manual |
-| Arquivos amigáveis ao Git | Sim | Sim | Parcial | Manual |
+| CLI / execução local | Sim | N/D | Não | Manual |
+| Arquivos compatíveis com o Git | Sim | Sim | Parcial | Manual |
| Sem dependência de SaaS | Sim | Sim | Não | Varia |
| Código aberto (AGPL-3.0) | Sim | Sim | Não | Varia |
-
+
## Licença
-[AGPL-3.0](LICENSE)
+[AGPL-3.0](../../LICENSE)
+Consulte [THIRD_PARTY_NOTICES.md](../../THIRD_PARTY_NOTICES.md) para avisos sobre dependências.
+
## Contribuição
-Consulte [CONTRIBUTING.md](CONTRIBUTING.md) para obter as diretrizes e a configuração de desenvolvimento. Todas as contribuições exigem a assinatura do DCO.
-
+Consulte [CONTRIBUTING.md](../../CONTRIBUTING.md) para ver as diretrizes e a configuração de desenvolvimento. Todas as contribuições exigem sign-off do DCO.
diff --git a/docs/i18n/ro.md b/docs/i18n/ro.md
index 58aa4e2..14f4db5 100644
--- a/docs/i18n/ro.md
+++ b/docs/i18n/ro.md
@@ -9,30 +9,31 @@
Flux de internaționalizare nativ AI pentru proiecte software. Traduceți, validați și gestionați fișierele i18n folosind LLM-uri.
[](https://github.com/Tom-R-Main/Internationalizer/actions/workflows/ci.yml)
-[](LICENSE)
+[](../../LICENSE)
-العربية · বাংলা · Deutsch · Español · Français · हिन्दी · Indonesia · 日本語 · ਪੰਜਾਬੀ · Português · Русский · తెలుగు
-ไทย · Українська · 粵語 · 简体中文 · 繁體中文
+العربية · বাংলা · Čeština · Dansk · Deutsch · Ελληνικά · Español · Suomi · Français · עברית · हिन्दी · Indonesia · Italiano · 日本語 · 한국어 · Bahasa Melayu
Nederlands · ਪੰਜਾਬੀ · Polski · Português · Română · Русский · Svenska · తెలుగు · ไทย · Türkçe · Українська · Tiếng Việt · 粵語 · 简体中文 · 繁體中文
---
+
## De ce Internationalizer?
-Majoritatea instrumentelor i18n sunt fie biblioteci de execuție (i18next, react-intl), fie platforme SaaS pentru gestionarea cheilor (Crowdin, Lokalise). Niciuna dintre ele nu rezolvă bine problema reală a traducerii:
+Majoritatea instrumentelor i18n sunt fie biblioteci pentru runtime (i18next, react-intl), fie platforme SaaS pentru gestionarea cheilor (Crowdin, Lokalise). Niciunul dintre ele nu rezolvă cu adevărat problema traducerii:
- **Traducerea manuală** nu este scalabilă dincolo de câteva limbi
-- **API-urile de traducere automată** (Google Translate, DeepL) vă ignoră terminologia, tonul și convențiile interfeței cu utilizatorul
-- **Traducerea LLM generică** funcționează mai bine, dar fără glosare și ghiduri de stil, obțineți rezultate inconsecvente
+- **API-urile de traducere automată** (Google Translate, DeepL) vă ignoră terminologia, tonul și convențiile interfeței utilizator
+- **Traducerea generică prin LLM** funcționează mai bine, însă fără glosare și ghiduri de stil obțineți rezultate inconsecvente
-Internationalizer este diferit. Este un **flux CLI** care combină traducerea LLM cu:
+Internationalizer este diferit. Este un **pipeline CLI** care combină traducerea prin LLM cu:
-- **Glosare specifice fiecărei limbi** — impun o terminologie consecventă în întreaga aplicație
-- **Ghiduri de stil specifice fiecărei limbi** — controlează tonul, nivelul de formalitate, pluralizarea și tipografia
-- **Memorie de traducere** — omite șirurile neschimbate, economisind bani la apelurile API
-- **Validarea cheilor** — detectează traducerile lipsă și nepotrivirile de interpolare înainte de lansare
+- **Glosare per limbă** — impun o terminologie consecventă în întreaga aplicație
+- **Ghiduri de stil per limbă** — controlează tonul, nivelul de formalitate, pluralul și tipografia
+- **Memorie de traducere** — omite șirurile neschimbate, economisind costurile apelurilor API
+- **Validare deterministă** — detectează cheile lipsă sau suplimentare, abaterile de structură protejată, problemele de glosar și erorile de plural sau ICU înainte de livrare
+
## Instalare
Instalați din npm:
@@ -41,13 +42,13 @@ Instalați din npm:
npm install -g internationalizer
```
-Sau rulați fără o instalare globală:
+Sau rulați fără instalare globală:
```bash
npx internationalizer --help
```
-Pachetul npm instalează binarul precompilat corespunzător din npm prin intermediul dependențelor opționale specifice platformei.
+Pachetul npm instalează binarul precompilat corespunzător din npm prin dependențe opționale specifice fiecărei platforme.
Instalați cu Go:
@@ -63,13 +64,15 @@ cd Internationalizer
go build -o internationalizer ./cmd/internationalizer
```
+
## Pachete npm
-- Etichetele Git și versiunile pachetelor npm trebuie să se potrivească, de exemplu `v0.1.0` și `0.1.0`
+- Etichetele Git și versiunile pachetelor npm trebuie să coincidă, de exemplu `v0.1.0` și `0.1.0`
- Pachetul rădăcină `internationalizer` depinde de pachete de platformă precum `internationalizer-darwin-arm64`
- Ținte npm acceptate: macOS arm64/x64, Linux arm64/x64, Windows x64
-- Publicarea CI necesită un secret GitHub numit `NPM_TOKEN`
+- Publicarea automată în CI necesită un secret GitHub denumit `NPM_TOKEN`
+
## Pornire rapidă
1. Creați un fișier de configurare în rădăcina proiectului:
@@ -78,7 +81,11 @@ go build -o internationalizer ./cmd/internationalizer
# .internationalizer.yml
source_locale: en
target_locales: [fr, de, es, ja]
-source_path: locales/en.json
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
llm:
provider: gemini
@@ -92,7 +99,7 @@ llm:
export GOOGLE_AI_STUDIO_API_KEY=your-ai-studio-key
```
-3. Previzualizați ce se va traduce:
+3. Previzualizați ce va fi tradus:
```bash
internationalizer translate --dry-run
@@ -104,49 +111,78 @@ internationalizer translate --dry-run
internationalizer translate
```
-5. Validați toate limbile:
+5. Validați toate pachetele de limbă:
```bash
internationalizer validate
```
+
## Comenzi
### `translate`
-Găsiți cheile lipsă și traduceți-le prin intermediul unui LLM.
+Găsiți cheile lipsă sau învechite și traduceți-le printr-un LLM.
```bash
internationalizer translate # translate all locales
internationalizer translate -l fr # translate French only
internationalizer translate --dry-run # preview without API calls
+internationalizer translate --adopt-existing # baseline existing translations without API calls
+internationalizer translate --refresh-policy # refresh prompt/style/model-stale entries
internationalizer translate --batch-size 20 # smaller batches
internationalizer translate --concurrency 2 # fewer parallel calls
```
+Starea traducerii raportează în mod independent stările lipsă, sursă învechită, politică învechită, actuală și editată manual, astfel încât o editare manuală nu poate masca o modificare a sursei sau a politicilor. Valorile cu politică învechită sunt raportate, dar sunt retraduse doar cu `--refresh-policy`. Valorile editate manual nu sunt niciodată suprascrise automat. Folosiți `--adopt-existing` atunci când asociați manifestul unor traduceri revizuite sau când acceptați în mod explicit o editare manuală revizuită drept nou nivel de referință.
+
### `validate`
-Verificați toate fișierele de limbă pentru chei lipsă, chei suplimentare și nepotriviri de interpolare.
+Verificați toate fișierele de limbă în raport cu pachetele sursă. Validarea implicită verifică acoperirea structurală (procentul de chei țintă obligatorii prezente), raportează cheile suplimentare ca avertismente și eșuează în caz de chei lipsă, nepotriviri de interpolare sau structură ICU MessageFormat nevalidă.
```bash
internationalizer validate # human-readable output
internationalizer validate --json # machine-readable JSON
internationalizer validate -q # exit code only
+internationalizer validate --strict # enforce translation quality rules
+internationalizer validate --require-state # require current manifest provenance
```
+`--strict` raportează și acoperirea tradusă. O valoare lingvistică identică cu sursa sa este considerată netradusă, cu excepția cazului în care glosarul conține explicit o intrare exactă cu aceeași sursă și aceeași țintă pentru valoarea completă; `ignore_case` este respectat, dar un termen de glosar inclus într-o valoare mai lungă nu constituie o scutire. Modul strict eșuează la chei suplimentare, valori identice cu sursa, structură de interpolare/HTML/cod/linkuri Markdown modificată, încălcări ale glosarului și lipsa formelor de plural configurate.
+
+`--require-state` verifică fiecare țintă în raport cu `.internationalizer.lock`. Eșuează când o cheie este neurmărită sau când hash-ul înregistrat pentru sursă, politica de traducere ori țintă este învechit. Poate fi combinat cu `--strict`.
+
+Rapoartele pentru utilizator și JSON folosesc coduri de constatare stabile:
+
+| Cod | Semnificație |
+| --- | --- |
+| `missing_key` / `extra_key` | Seturile de chei sursă și țintă diferă |
+| `blank_translation` | O sursă nevidă are o țintă goală în modul strict |
+| `source_identical` | O valoare lingvistică în modul strict rămâne netradusă |
+| `protected_structure_mismatch` | Structura de interpolare, HTML, cod sau linkuri a fost modificată |
+| `glossary_violation` | Nu a fost găsit niciun termen țintă sau nicio variantă aprobată |
+| `plural_form_missing` | O formă de plural configurată pentru limbă lipsește |
+| `icu_message_syntax` | Un mesaj ICU sursă sau țintă este formatat incorect |
+| `icu_argument_mismatch` | Numele argumentelor ICU, tipurile sau stilurile de formatare diferă |
+| `icu_selector_mismatch` | Selectorii diferă sau o categorie de plural este nevalidă pentru limba țintă |
+| `untracked` | Nu există nicio înregistrare în manifest pentru țintă |
+| `source_stale` | Conținutul sursă s-a modificat după înregistrarea traducerii |
+| `policy_stale` | Promptul generat sau setările modelului s-au modificat |
+| `target_modified` | Conținutul țintă diferă de înregistrarea din manifest |
+
### `detect`
-Detectați automat framework-ul i18n și generați o sugestie de configurare.
+Detectați automat framework-ul i18n și sugerați o configurație.
```bash
internationalizer detect
```
-Acceptă: react-i18next, next-intl, vue-i18n, JSON simplu, documente Markdown.
+Acceptă: react-i18next, next-intl, vue-i18n, JSON simplu, documentații Markdown.
### `glossary`
-Gestionați termenii de glosar specifici fiecărei limbi, care sunt impuși în timpul traducerii.
+Gestionați termenii de glosar per limbă a căror aplicare este impusă în timpul traducerii.
```bash
internationalizer glossary list --locale fr
@@ -156,7 +192,7 @@ internationalizer glossary remove --locale fr --source "Dashboard"
### `tm`
-Gestionați memoria de traducere (cache JSONL al șirurilor traduse anterior).
+Gestionați memoria de traducere (cache JSONL cu șiruri traduse anterior).
```bash
internationalizer tm stats # show record counts
@@ -164,6 +200,7 @@ internationalizer tm export # dump as JSON
internationalizer tm clear --force # delete all records
```
+
## Referință de configurare
```yaml
@@ -173,10 +210,23 @@ internationalizer tm clear --force # delete all records
source_locale: en
# Languages to translate into (required)
-target_locales: [fr, de, es, ja, zh-CN, ar]
-
-# Path to the source locale file (required)
-source_path: locales/en.json
+target_locales: [fr, de, es, ja, yue, zh-CN, zh-TW, ar]
+
+# One or more source-to-target mappings (required).
+# {locale} is replaced with each configured target locale.
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
+ - id: docs
+ source: README.md
+ target: docs/i18n/{locale}.md
+ format: markdown
+
+# Backward compatibility: source_path still maps targets to sibling files
+# such as locales/fr.json. Prefer bundles for new projects.
+# source_path: locales/en.json
# LLM provider settings
llm:
@@ -185,7 +235,7 @@ llm:
# Model name defaults by provider:
# anthropic: claude-opus-5
- # openai: gpt-5.6-luna
+ # openai: gpt-5.6-luna (reasoning effort defaults to max)
# gemini: gemini-3.8-flash
# openrouter: deepseek/deepseek-v4-pro-0813
model: gemini-3.8-flash
@@ -196,6 +246,27 @@ llm:
# Base URL for OpenAI-compatible endpoints (optional)
# base_url: https://api.openai.com
+ # OpenAI GPT-5-series Responses API reasoning effort
+ # (default: max for the OpenAI provider)
+ reasoning_effort: max
+
+ # Optional LLM settings for individual target locales. An override using the
+ # global provider inherits unspecified global settings. A different provider
+ # uses that provider's defaults for unspecified settings.
+ locale_overrides:
+ yue:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-CN:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-TW:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+
# Keys per LLM call (default: 40)
batch_size: 40
@@ -210,11 +281,26 @@ glossary_dir: glossary
# Path to translation memory file (default: .internationalizer/tm.jsonl)
tm_path: .internationalizer/tm.jsonl
+
+# Versioned source, policy, target, and provenance state
+# (default: .internationalizer.lock; commit this file)
+manifest_path: .internationalizer.lock
+
+# Optional translation and strict-validation rules
+validation:
+ plural_style: i18next-v4 # generate and validate target-locale plural forms
```
+Identificatorii de limbă trebuie să fie etichete BCP 47 valide, precum `fr`, `pt-BR` sau `sr-Latn-RS`. Limbile țintă echivalente canonic sunt respinse ca duplicate, iar suprascrierile de furnizor specifice unei limbi se potrivesc cu scrierea canonică echivalentă. În exemplul de mai sus, limbile fără o configurare dedicată — inclusiv japoneza — moștenesc configurația globală Gemini.
+
+Valorile ICU MessageFormat sunt analizate structural. Argumentele simple, `select`, `plural`, `selectordinal`, `number`, `date` și `time` sunt acceptate, inclusiv mesajele imbricate, decalajele de plural, selectorii numerici exacți și `#`. Validarea verifică sintaxa, tipurile de argumente și stilurile de formatare, decalajele de plural, identitatea ramurilor select și categoriile de plural CLDR pentru limba țintă. Răspunsul furnizorului care încalcă aceste condiții invariante este respins înainte de a fi scris un fișier de limbă sau o înregistrare în memoria de traducere.
+
+Cu `i18next-v4`, familiile sursă de plural recunoscute sunt extinse în timpul traducerii la categoriile CLDR ale limbii țintă. O categorie existentă doar în limba țintă folosește valoarea `_other` a familiei sursă drept șablon de traducere. Validarea strictă impune acele categorii țintă; categoriile existente doar în sursă sunt opționale pentru limbile țintă care nu le folosesc.
+
+
## Ghiduri de stil
-Ghidurile de stil sunt fișiere Markdown care sunt injectate în promptul de traducere LLM. Acestea controlează tonul, nivelul de formalitate, tipografia și alte convenții specifice limbii.
+Ghidurile de stil sunt fișiere Markdown transmise în promptul de traducere trimis LLM-ului. Ele controlează tonul, nivelul de formalitate, tipografia și alte convenții specifice fiecărei limbi.
```
style-guides/
@@ -224,17 +310,20 @@ style-guides/
ar.md # Arabic-specific rules
```
-### Convenții partajate (`_conventions.md`)
+### Convenții comune (`_conventions.md`)
-Definiți regulile care se aplică tuturor limbilor: sintaxa de interpolare, păstrarea HTML, convențiile pentru tipurile de șiruri (butoane vs. etichete vs. erori) etc.
+Definiți reguli aplicabile tuturor limbilor: sintaxa de interpolare, păstrarea HTML, convențiile privind tipurile de șiruri (butoane vs. etichete vs. erori) etc.
-### Ghiduri specifice fiecărei limbi (`{locale}.md`)
+### Ghiduri per limbă (`{locale}.md`)
-Definiți regulile specifice limbii: registrul de formalitate (tu vs. vous), punctuația (ghilimele, semne de întrebare inversate), formele de plural, formatarea datelor/numerelor și un glosar terminologic.
+Definiți reguli specifice limbii: registrul de adresare (tu vs. dumneavoastră), punctuația (ghilimele, semne de întrebare inversate), formele de plural, formatarea datei și a numerelor și un glosar terminologic.
-Consultați [`examples/react-app/style-guides/`](examples/react-app/style-guides/) pentru un exemplu funcțional.
+Ghidurile de stil sunt date de intrare durabile pentru politici, nu conținut generat. Internationalizer le citește, dar nu le suprascrie niciodată. Conținutul lor este asociat unui hash calculat separat de glosar și contractul de prompt, astfel încât o modificare a codului aplicației nu face o traducere să devină învechită. Editarea unui ghid marchează intenționat respectiva limbă pentru revizuirea politicii; ajustarea formulării interne a promptului nu face acest lucru, cu excepția cazului în care se modifică și versiunea contractului de prompt.
-## Formatul glosarului
+Consultați [`examples/react-app/style-guides/`](../../examples/react-app/style-guides/) pentru un exemplu funcțional.
+
+
+## Formatul glosarelor
Fișierele de glosar sunt matrice JSON stocate în `{glossary_dir}/{locale}.json`:
@@ -243,41 +332,49 @@ Fișierele de glosar sunt matrice JSON stocate în `{glossary_dir}/{locale}.json
{
"source": "Dashboard",
"target": "Tableau de bord",
+ "variants": ["Panneau de contrôle"],
+ "enforcement": "error",
"ignore_case": false,
"whole_word": true
}
]
```
-Termenii sunt injectați în promptul LLM sub forma unui tabel terminologic, asigurând o traducere consecventă a termenilor cheie în întreaga aplicație.
+`variants` listează alte forme aprobate în limba țintă. `enforcement` poate fi `error`, `warning` sau poate fi omisă pentru comportamentul implicit de eroare. Termenii sunt transmiși în promptul LLM sub forma unui tabel de terminologie, asigurând o traducere consecventă în întreaga aplicație. O intrare exactă precum `{"source":"API","target":"API"}` scutește, de asemenea, acea valoare completă identică cu sursa de la constatările de valori netraduse din modul strict; nu scutește însă o valoare mai lungă care conține doar `API`.
+
## Memorie de traducere
-Memoria de traducere este stocată ca un fișier JSONL (o înregistrare JSON pe linie). Fiecare înregistrare conține:
+Memoria de traducere este stocată sub forma unui fișier JSONL (o înregistrare JSON pe fiecare linie). Fiecare înregistrare conține:
-- Cheia și valoarea sursă
-- Valoarea tradusă
-- Un hash SHA-256 al valorii sursă
-- Un marcaj de timp
+- Pachetul, cheia, valoarea sursă, valoarea tradusă și codul canonic al limbii țintă
+- Hash-urile pentru sursă, ghidul de stil, glosar, contractul de prompt și hash-ul combinat de politică
+- Furnizorul și modelul care au produs traducerea
+- Un marcaj temporal
-La rulările ulterioare, șirurile neschimbate sunt servite din memoria cache TM fără a apela LLM-ul, economisind atât timp, cât și costuri API. Fișierul TM este compatibil cu Git și poate fi inclus în commit-uri alături de fișierele de limbă.
+La rulările ulterioare, șirurile cu aceleași hash-uri de sursă și politică sunt servite direct din cache, fără a apela LLM-ul. Calea implicită se află în directorul ignorat `.internationalizer/`, astfel încât rămâne un cache local. Setați `tm_path` către o locație urmărită prin controlul versiunilor dacă proiectul partajează intenționat memoria de traducere. Manifestul revizuibil `.internationalizer.lock` este versionat separat.
+
## Formate acceptate
| Format | Extensii | Mod |
|--------|-----------|------|
| JSON | `.json` | Cheie-valoare (imbricate, aplatizate prin notație cu punct) |
-| YAML | `.yml`, `.yaml` | Cheie-valoare (păstrează comentariile și ordinea) |
-| Markdown | `.md`, `.mdx` | Traducerea întregului document |
+| YAML | `.yml`, `.yaml` | Cheie-valoare (păstrează comentariile și ordonarea) |
+| Markdown | `.md`, `.mdx` | Preambul și secțiuni la nivel H2 |
+
+Fișierele Markdown țintă conțin comentarii invizibile `internationalizer:unit` înainte de secțiunile H2. Acești markeri stabili îi permit instrumentului Internationalizer să adauge, să mute sau să editeze o secțiune sursă fără a retraduce secțiunile neafectate. Documentele existente nemarcate primesc markeri la următoarea actualizare reușită.
+
## Detectarea tipului de proiect
-`internationalizer detect` identifică configurația i18n verificând:
+`internationalizer detect` identifică configurarea dumneavoastră i18n verificând:
- Dependențele din `package.json` pentru react-i18next, next-intl sau vue-i18n
-- Structurile de directoare care se potrivesc cu modelele comune de limbă
+- Structurile de directoare care corespund modelelor comune de configurare a limbilor
- Extensiile de fișiere și convențiile de denumire
+
## Arhitectură
```
@@ -292,30 +389,38 @@ internal/
openai.go OpenAI / compatible backend
gemini.go Google Gemini via AI Studio backend
OpenRouter uses openai.go with custom base_url
+ locale/ BCP 47 identity and CLDR plural categories
+ message/ ICU MessageFormat parser and structural comparison
+ policy/ Stable translation-policy hashing
+ state/ Versioned translation manifest
styleguide/ Style guide loader
tm/ JSONL translation memory
translate/ Translation orchestrator
validate/ Locale validation and diffing
```
+
## Comparație cu alternativele
| Funcționalitate | Internationalizer | i18next | Crowdin | LLM generic |
|---------|------------------|---------|---------|-------------|
| Traducere bazată pe LLM | Da | Nu | Parțial | Da |
-| Ghiduri de stil specifice limbii | Da | Nu | Nu | Nu |
-| Impunerea glosarului | Da | Nu | Da | Nu |
+| Ghiduri de stil per limbă | Da | Nu | Nu | Nu |
+| Impunerea terminologiei din glosar | Da | Nu | Da | Nu |
| Memorie de traducere | Da | Nu | Da | Nu |
-| CLI / execuție locală | Da | N/A | Nu | Manual |
-| Fișiere compatibile cu Git | Da | Da | Parțial | Manual |
+| Rulare CLI / locală | Da | N/A | Nu | Manual |
+| Fișiere adaptate pentru Git | Da | Da | Parțial | Manual |
| Fără dependență de SaaS | Da | Da | Nu | Variază |
| Open source (AGPL-3.0) | Da | Da | Nu | Variază |
+
## Licență
-[AGPL-3.0](LICENSE)
+[AGPL-3.0](../../LICENSE)
-## Contribuții
+Consultați [THIRD_PARTY_NOTICES.md](../../THIRD_PARTY_NOTICES.md) pentru notificările privind dependențele.
-Consultați [CONTRIBUTING.md](CONTRIBUTING.md) pentru configurarea mediului de dezvoltare și instrucțiuni. Toate contribuțiile necesită aprobarea DCO.
+
+## Contribuții
+Consultați [CONTRIBUTING.md](../../CONTRIBUTING.md) pentru configurarea mediului de dezvoltare și ghiduri. Toate contribuțiile necesită asumarea DCO.
diff --git a/docs/i18n/ru.md b/docs/i18n/ru.md
index 7520140..a8a3137 100644
--- a/docs/i18n/ru.md
+++ b/docs/i18n/ru.md
@@ -6,41 +6,47 @@
# Internationalizer
-AI-нативный пайплайн интернационализации для программных проектов. Переводите, проверяйте и управляйте файлами i18n с помощью LLM.
+AI-нативный конвейер интернационализации для программных проектов. Переводите, проверяйте и обновляйте файлы i18n с помощью LLM.
[](https://github.com/Tom-R-Main/Internationalizer/actions/workflows/ci.yml)
-[](LICENSE)
+[](../../LICENSE)
-## Почему Internationalizer?
+
+العربية · বাংলা · Čeština · Dansk · Deutsch · Ελληνικά · Español · Suomi · Français · עברית · हिन्दी · Indonesia · Italiano · 日本語 · 한국어 · Bahasa Melayu
Nederlands · ਪੰਜਾਬੀ · Polski · Português · Română · Русский · Svenska · తెలుగు · ไทย · Türkçe · Українська · Tiếng Việt · 粵語 · 简体中文 · 繁體中文
+
-Большинство инструментов i18n — это либо библиотеки времени выполнения (i18next, react-intl), либо SaaS-платформы для управления ключами (Crowdin, Lokalise). Ни один из них не решает саму проблему перевода должным образом:
+---
+
+## Почему Internationalizer?
-- **Ручной перевод** плохо масштабируется при работе с более чем несколькими языками
-- **API машинного перевода** (Google Translate, DeepL) игнорируют вашу терминологию, тон и соглашения пользовательского интерфейса
-- **Обычный перевод с помощью LLM** работает лучше, но без глоссариев и руководств по стилю вы получаете противоречивые результаты
+Большинство инструментов i18n — это либо runtime-библиотеки (i18next, react-intl), либо SaaS-платформы для управления ключами (Crowdin, Lokalise). Ни один из них не решает задачу качественного перевода:
-Internationalizer устроен иначе. Это **CLI-пайплайн**, который объединяет LLM-перевод с:
+- **Ручной перевод** перестает масштабироваться, когда языков становится больше нескольких
+- **API машинного перевода** (Google Translate, DeepL) не учитывают терминологию, тональность и интерфейсные соглашения продукта
+- **Обычный перевод через LLM** справляется лучше, но без глоссариев и руководств по стилю выдает несогласованные результаты
-- **Глоссариями для каждого языка** — обеспечивают единообразную терминологию во всем приложении
-- **Руководствами по стилю для каждого языка** — контролируют тон, формальность, плюрализацию и типографику
-- **Памятью переводов (Translation memory)** — пропускает неизмененные строки, экономя деньги на вызовах API
-- **Валидацией ключей** — выявляет отсутствующие переводы и несовпадения интерполяции до релиза
+Internationalizer устроен иначе. Это **CLI-конвейер**, сочетающий перевод через LLM со следующими возможностями:
+- **Глоссарии для каждого языка** — единая и выверенная терминология во всем приложении
+- **Руководства по стилю для каждого языка** — контроль тональности, регистра вежливости, форм множественного числа и типографики
+- **Память переводов (Translation memory)** — пропуск неизмененных строк и прямая экономия на вызовах API
+- **Детерминированная валидация** — выявление отсутствующих или лишних ключей, искажений защищенных структур, нарушений глоссария и ошибок в ICU или формах множественного числа до релиза
+
## Установка
-Установка из npm:
+Установка через npm:
```bash
npm install -g internationalizer
```
-Или запуск без глобальной установки:
+Запуск без глобальной установки:
```bash
npx internationalizer --help
```
-Пакет npm устанавливает соответствующий предварительно собранный бинарный файл из npm через платформозависимые опциональные зависимости.
+Пакет npm автоматически устанавливает подходящий предсобранный бинарный файл из реестра через платформозависимые опциональные зависимости.
Установка с помощью Go:
@@ -48,30 +54,34 @@ npx internationalizer --help
go install github.com/Tom-R-Main/Internationalizer/cmd/internationalizer@latest
```
-Или сборка из исходного кода:
+Сборка из исходного кода:
```bash
git clone https://github.com/Tom-R-Main/Internationalizer.git
cd Internationalizer
go build -o internationalizer ./cmd/internationalizer
```
-
+
## Пакеты npm
- Теги Git и версии пакетов npm должны совпадать, например `v0.1.0` и `0.1.0`
- Корневой пакет `internationalizer` зависит от платформенных пакетов, таких как `internationalizer-darwin-arm64`
-- Поддерживаемые целевые платформы npm: macOS arm64/x64, Linux arm64/x64, Windows x64
-- Для публикации в CI требуется секрет GitHub с именем `NPM_TOKEN`
-
+- Поддерживаемые платформы npm: macOS arm64/x64, Linux arm64/x64, Windows x64
+- Для публикации через CI требуется секрет GitHub с именем `NPM_TOKEN`
+
## Быстрый старт
-1. Создайте файл конфигурации в корне вашего проекта:
+1. Создайте файл конфигурации в корне проекта:
```yaml
# .internationalizer.yml
source_locale: en
target_locales: [fr, de, es, ja]
-source_path: locales/en.json
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
llm:
provider: gemini
@@ -79,13 +89,13 @@ llm:
api_key_env: GOOGLE_AI_STUDIO_API_KEY
```
-2. Установите ваш API-ключ:
+2. Задайте ключ API в переменной окружения:
```bash
export GOOGLE_AI_STUDIO_API_KEY=your-ai-studio-key
```
-3. Предварительный просмотр того, что будет переведено:
+3. Посмотрите, какие строки будут переведены:
```bash
internationalizer translate --dry-run
@@ -102,44 +112,72 @@ internationalizer translate
```bash
internationalizer validate
```
-
+
## Команды
### `translate`
-Поиск отсутствующих ключей и их перевод с помощью LLM.
+Поиск отсутствующих или устаревших ключей и их перевод с помощью LLM.
```bash
-internationalizer translate # перевести все локали
-internationalizer translate -l fr # перевести только на французский
-internationalizer translate --dry-run # предпросмотр без вызовов API
-internationalizer translate --batch-size 20 # меньший размер пакета (батча)
-internationalizer translate --concurrency 2 # меньше параллельных вызовов
+internationalizer translate # translate all locales
+internationalizer translate -l fr # translate French only
+internationalizer translate --dry-run # preview without API calls
+internationalizer translate --adopt-existing # baseline existing translations without API calls
+internationalizer translate --refresh-policy # refresh prompt/style/model-stale entries
+internationalizer translate --batch-size 20 # smaller batches
+internationalizer translate --concurrency 2 # fewer parallel calls
```
+Состояние перевода раздельно отслеживает пять условий: отсутствие перевода, устаревание исходника, устаревание политики, актуальное состояние и ручное редактирование. Благодаря этому ручная правка не может скрыть изменение исходного текста или правил перевода. Устаревшие по политике значения выводятся в отчете, но повторно переводятся только при указании флага `--refresh-policy`. Отредактированные вручную значения никогда не перезаписываются автоматически. Используйте `--adopt-existing`, чтобы впервые зафиксировать проверенные переводы в манифесте или явно утвердить выверенную ручную правку как новый базовый уровень.
+
### `validate`
-Проверка всех файлов локалей на наличие отсутствующих ключей, лишних ключей и несовпадений интерполяции.
+Сверка всех файлов локалей с исходными бандлами. По умолчанию валидация проверяет структурное покрытие (процент присутствующих обязательных целевых ключей), сообщает о лишних ключах в виде предупреждений и завершается с ошибкой при пропущенных ключах, несовпадении переменных интерполяции или неверной структуре ICU MessageFormat.
```bash
-internationalizer validate # человекочитаемый вывод
-internationalizer validate --json # машиночитаемый JSON
-internationalizer validate -q # только код возврата
+internationalizer validate # human-readable output
+internationalizer validate --json # machine-readable JSON
+internationalizer validate -q # exit code only
+internationalizer validate --strict # enforce translation quality rules
+internationalizer validate --require-state # require current manifest provenance
```
+Флаг `--strict` дополнительно сообщает процент переведенного содержимого. Любой текстовый фрагмент, идентичный исходнику, считается непереведенным, за исключением случаев, когда в глоссарии явно прописано точное совпадение исходного и целевого значения для всей строки целиком; параметр `ignore_case` учитывается, но вхождение термина глоссария в более длинный текст исключением не признается. Строгий режим завершается ошибкой при наличии лишних ключей, совпадении перевода с исходником, изменении структуры интерполяции, HTML, кода или ссылок Markdown, а также при нарушениях глоссария и отсутствии настроенных форм множественного числа.
+
+Флаг `--require-state` сверяет каждый целевой ключ с манифестом `.internationalizer.lock`. Команда завершается ошибкой, если ключ не отслеживается либо если устарел его записанный исходник, правила перевода или хеш переведенного значения. Этот режим можно сочетать с `--strict`.
+
+В текстовых отчетах и JSON используются стабильные коды находок:
+
+| Код | Значение |
+| --- | --- |
+| `missing_key` / `extra_key` | Наборы ключей в исходном файле и целевом переводе различаются |
+| `blank_translation` | Непустому исходному тексту соответствует пустой перевод в строгом режиме |
+| `source_identical` | Текстовое значение в строгом режиме осталось непереведенным |
+| `protected_structure_mismatch` | Изменилась структура интерполяции, HTML, кода или ссылок |
+| `glossary_violation` | Не найден утвержденный термин глоссария или его вариант |
+| `plural_form_missing` | Отсутствует настроенная форма множественного числа для локали |
+| `icu_message_syntax` | Исходное или переведенное сообщение ICU сформировано неверно |
+| `icu_argument_mismatch` | Различаются имена, типы или стили форматирования аргументов ICU |
+| `icu_selector_mismatch` | Различаются селекторы или категория множественного числа недопустима для целевой локали |
+| `untracked` | В манифесте отсутствует запись для целевого значения |
+| `source_stale` | Исходный текст изменился после зафиксированного перевода |
+| `policy_stale` | Изменился сформированный промпт или параметры модели |
+| `target_modified` | Переведенное значение отличается от записи в манифесте |
+
### `detect`
-Автоматическое определение фреймворка i18n и предложение конфигурации.
+Автоматическое определение используемого фреймворка i18n и подготовка рекомендуемой конфигурации.
```bash
internationalizer detect
```
-Поддерживает: react-i18next, next-intl, vue-i18n, обычный JSON, документацию в Markdown.
+Поддерживаются: react-i18next, next-intl, vue-i18n, стандартный JSON, документация в Markdown.
### `glossary`
-Управление терминами глоссария для каждого языка, которые применяются во время перевода.
+Управление языковыми терминами глоссария, соблюдение которых обеспечивается при переводе.
```bash
internationalizer glossary list --locale fr
@@ -152,163 +190,222 @@ internationalizer glossary remove --locale fr --source "Dashboard"
Управление памятью переводов (кэш ранее переведенных строк в формате JSONL).
```bash
-internationalizer tm stats # показать количество записей
-internationalizer tm export # выгрузить в формате JSON
-internationalizer tm clear --force # удалить все записи
+internationalizer tm stats # show record counts
+internationalizer tm export # dump as JSON
+internationalizer tm clear --force # delete all records
```
-
+
## Справочник по конфигурации
```yaml
# .internationalizer.yml
-# Исходный язык (по умолчанию: en)
+# Source language (default: en)
source_locale: en
-# Языки для перевода (обязательно)
-target_locales: [fr, de, es, ja, zh-CN, ar]
-
-# Путь к исходному файлу локали (обязательно)
-source_path: locales/en.json
-
-# Настройки провайдера LLM
+# Languages to translate into (required)
+target_locales: [fr, de, es, ja, yue, zh-CN, zh-TW, ar]
+
+# One or more source-to-target mappings (required).
+# {locale} is replaced with each configured target locale.
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
+ - id: docs
+ source: README.md
+ target: docs/i18n/{locale}.md
+ format: markdown
+
+# Backward compatibility: source_path still maps targets to sibling files
+# such as locales/fr.json. Prefer bundles for new projects.
+# source_path: locales/en.json
+
+# LLM provider settings
llm:
- # Провайдер: "anthropic", "openai", "gemini" или "openrouter" (по умолчанию: gemini)
+ # Provider: "anthropic", "openai", "gemini", or "openrouter" (default: gemini)
provider: gemini
- # Имена моделей по умолчанию для провайдеров:
+ # Model name defaults by provider:
# anthropic: claude-opus-5
- # openai: gpt-5.6-luna
+ # openai: gpt-5.6-luna (reasoning effort defaults to max)
# gemini: gemini-3.8-flash
# openrouter: deepseek/deepseek-v4-pro-0813
model: gemini-3.8-flash
- # Переменная окружения, содержащая API-ключ
+ # Environment variable containing the API key
api_key_env: GOOGLE_AI_STUDIO_API_KEY
- # Базовый URL для OpenAI-совместимых эндпоинтов (опционально)
+ # Base URL for OpenAI-compatible endpoints (optional)
# base_url: https://api.openai.com
-# Количество ключей на один вызов LLM (по умолчанию: 40)
+ # OpenAI GPT-5-series Responses API reasoning effort
+ # (default: max for the OpenAI provider)
+ reasoning_effort: max
+
+ # Optional LLM settings for individual target locales. An override using the
+ # global provider inherits unspecified global settings. A different provider
+ # uses that provider's defaults for unspecified settings.
+ locale_overrides:
+ yue:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-CN:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-TW:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+
+# Keys per LLM call (default: 40)
batch_size: 40
-# Параллельные вызовы LLM (по умолчанию: 4)
+# Parallel LLM calls (default: 4)
concurrency: 4
-# Директория с Markdown-файлами руководств по стилю для каждой локали (по умолчанию: style-guides)
+# Directory containing per-locale style guide Markdown files (default: style-guides)
style_guides_dir: style-guides
-# Директория с JSON-файлами глоссариев для каждой локали (по умолчанию: glossary)
+# Directory containing per-locale glossary JSON files (default: glossary)
glossary_dir: glossary
-# Путь к файлу памяти переводов (по умолчанию: .internationalizer/tm.jsonl)
+# Path to translation memory file (default: .internationalizer/tm.jsonl)
tm_path: .internationalizer/tm.jsonl
+
+# Versioned source, policy, target, and provenance state
+# (default: .internationalizer.lock; commit this file)
+manifest_path: .internationalizer.lock
+
+# Optional translation and strict-validation rules
+validation:
+ plural_style: i18next-v4 # generate and validate target-locale plural forms
```
+Идентификаторы локалей должны быть корректными тегами BCP 47, такими как `fr`, `pt-BR` или `sr-Latn-RS`. Канонически эквивалентные целевые локали отклоняются как дубликаты, а переопределения провайдера для конкретной локали сопоставляются с учетом канонической эквивалентности. В приведенном выше примере локали без индивидуального переопределения (включая японскую) наследуют общую конфигурацию Gemini.
+
+Значения в формате ICU MessageFormat разбираются структурно. Поддерживаются простые аргументы, `select`, `plural`, `selectordinal`, `number`, `date` и `time`, включая вложенные сообщения, смещения во множественном числе (plural offsets), точные числовые селекторы и символ `#`. Валидация проверяет синтаксис, типы аргументов и стили форматирования, смещения во множественном числе, совпадение веток select и категории множественного числа CLDR для целевой локали. Ответ провайдера, нарушающий эти инварианты, отклоняется до записи в файл локали или в память переводов.
+
+В режиме `i18next-v4` распознанные группы множественного числа из исходника при переводе расширяются до категорий CLDR целевой локали. Если категория существует только в целевом языке, шаблоном для ее перевода служит значение `_other` из исходной группы. Строгая валидация требует наличия всех целевых категорий; категории, присутствующие только в исходнике, необязательны для тех целевых локалей, где они не используются.
+
## Руководства по стилю
-Руководства по стилю — это Markdown-файлы, которые внедряются в промпт для LLM-перевода. Они контролируют тон, формальность, типографику и другие языковые соглашения.
+Руководства по стилю представляют собой файлы Markdown, которые передаются в системный промпт при переводе через LLM. Они задают тональность, регистр вежливости, типографику и другие языковые нормы.
```
style-guides/
- _conventions.md # общие правила для всех языков
- fr.md # правила для французского языка
- ja.md # правила для японского языка
- ar.md # правила для арабского языка
+ _conventions.md # shared rules for all languages
+ fr.md # French-specific rules
+ ja.md # Japanese-specific rules
+ ar.md # Arabic-specific rules
```
### Общие соглашения (`_conventions.md`)
-Определяют правила, применимые ко всем языкам: синтаксис интерполяции, сохранение HTML, соглашения о типах строк (кнопки, метки, ошибки) и т. д.
+Задают правила, действующие для всех языков: синтаксис интерполяции, сохранение тегов HTML, соглашения по типам строк (кнопки, метки, сообщения об ошибках) и т. д.
-### Руководства для конкретных языков (`{locale}.md`)
+### Руководства для отдельных языков (`{locale}.md`)
-Определяют специфичные для языка правила: регистр формальности (ты/вы), пунктуацию (кавычки-елочки, перевернутые вопросительные знаки), формы множественного числа, форматирование дат/чисел и терминологический глоссарий.
+Задают нормы конкретного языка: регистр вежливости («ты» или «вы»), пунктуацию («кавычки-елочки», перевернутые вопросительные знаки), формы множественного числа, форматы дат и чисел, а также терминологический глоссарий.
-Смотрите [`examples/react-app/style-guides/`](examples/react-app/style-guides/) для ознакомления с рабочим примером.
+Руководства по стилю — это долговременные входные правила, а не генерируемый результат. Internationalizer считывает их, но никогда не перезаписывает. Их содержимое хешируется отдельно от глоссария и контракта промпта, поэтому изменения в коде приложения не приводят к устареванию переводов. Редактирование руководства намеренно помечает соответствующую локаль для пересмотра правил; изменение внутренних формулировок промпта к этому не приводит, если только не изменилась версия самого контракта промпта.
+Рабочий пример структуры приведен в каталоге [`examples/react-app/style-guides/`](../../examples/react-app/style-guides/).
+
## Формат глоссария
-Файлы глоссария представляют собой массивы JSON, хранящиеся в `{glossary_dir}/{locale}.json`:
+Файлы глоссария представляют собой массивы JSON, хранящиеся по пути `{glossary_dir}/{locale}.json`:
```json
[
{
"source": "Dashboard",
"target": "Tableau de bord",
+ "variants": ["Panneau de contrôle"],
+ "enforcement": "error",
"ignore_case": false,
"whole_word": true
}
]
```
-Термины внедряются в промпт LLM в виде терминологической таблицы, обеспечивая единообразный перевод ключевых терминов во всем вашем приложении.
-
+В массиве `variants` перечисляются другие допустимые формы перевода. Поле `enforcement` может принимать значения `error`, `warning` либо опускаться (по умолчанию действует режим ошибки). Термины подставляются в промпт LLM в виде таблицы соответствий, гарантируя единообразный перевод во всем приложении. Точная запись вида `{"source":"API","target":"API"}` также освобождает полностью совпадающее исходное значение от ошибок непереведенного текста в строгом режиме; при этом частичное вхождение `API` в более длинную строку исключением не является.
+
## Память переводов
-Память переводов (Translation memory) хранится в виде файла JSONL (одна запись JSON на строку). Каждая запись содержит:
+Память переводов хранится в файле формата JSONL (одна запись JSON на строку). Каждая запись содержит:
-- Исходный ключ и значение
-- Переведенное значение
-- SHA-256 хэш исходного значения
+- Бандл, ключ, исходный текст, переведенный текст и каноническую целевую локаль
+- Хеши исходного текста, руководства по стилю, глоссария, контракта промпта и общий хеш политики
+- Провайдера и модель, выполнившие перевод
- Временную метку
-При последующих запусках неизмененные строки берутся из кэша TM без вызова LLM, что экономит время и затраты на API. Файл TM удобен для работы с git и может быть закоммичен вместе с файлами локалей.
-
+При повторных запусках строки с неизменившимися хешами исходника и правил берутся из кэша без обращения к LLM. По умолчанию файл хранится в игнорируемом каталоге `.internationalizer/` в качестве локального кэша. Если проект предполагает совместное использование памяти переводов в команде, укажите в `tm_path` путь к отслеживаемому файлу. Манифест `.internationalizer.lock`, предназначенный для код-ревью, версионируется независимо.
+
## Поддерживаемые форматы
| Формат | Расширения | Режим |
-|--------|-----------|------|
-| JSON | `.json` | Ключ-значение (вложенные, плоские с точечной нотацией) |
-| YAML | `.yml`, `.yaml` | Ключ-значение (сохраняет комментарии и порядок) |
-| Markdown | `.md`, `.mdx` | Перевод документа целиком |
+| --- | --- | --- |
+| JSON | `.json` | Ключ-значение (вложенные структуры, плоские ключи с точечной нотацией) |
+| YAML | `.yml`, `.yaml` | Ключ-значение (сохранение комментариев и порядка следования) |
+| Markdown | `.md`, `.mdx` | Преамбула и разделы уровня H2 |
+В целевых файлах Markdown перед разделами H2 расставляются невидимые комментарии `internationalizer:unit`. Эти стабильные метки позволяют добавлять, перемещать или редактировать отдельные разделы оригинала без повторного перевода остального документа. В существующие файлы без разметки маркеры добавляются при очередном успешном обновлении.
+
## Определение типа проекта
-`internationalizer detect` определяет вашу конфигурацию i18n путем проверки:
-
-- Зависимостей в `package.json` на наличие react-i18next, next-intl или vue-i18n
-- Структуры каталогов, соответствующей общим паттернам локалей
-- Расширений файлов и соглашений об именовании
+Команда `internationalizer detect` определяет структуру i18n в проекте по следующим признакам:
+- Наличие зависимостей react-i18next, next-intl или vue-i18n в файле `package.json`
+- Структура каталогов, соответствующая типовым шаблонам локализации
+- Расширения файлов и соглашения об именовании
+
## Архитектура
```
-cmd/internationalizer/ Точка входа CLI и определения команд
+cmd/internationalizer/ CLI entry point and command definitions
internal/
- config/ Загрузка YAML-конфигурации со значениями по умолчанию
- detect/ Автоопределение типа проекта
- formats/ Парсеры форматов (JSON, YAML, Markdown)
- glossary/ Управление глоссариями для каждой локали
- llm/ Интерфейс провайдера LLM + реализации
- anthropic.go Бэкенд Anthropic Claude
- openai.go Бэкенд OpenAI / совместимый
- gemini.go Бэкенд Google Gemini через AI Studio
- OpenRouter использует openai.go с кастомным base_url
- styleguide/ Загрузчик руководств по стилю
- tm/ Память переводов JSONL
- translate/ Оркестратор перевода
- validate/ Валидация локалей и сравнение
+ config/ YAML config loading with defaults
+ detect/ Project type auto-detection
+ formats/ Format parsers (JSON, YAML, Markdown)
+ glossary/ Per-locale glossary management
+ llm/ LLM provider interface + implementations
+ anthropic.go Anthropic Claude backend
+ openai.go OpenAI / compatible backend
+ gemini.go Google Gemini via AI Studio backend
+ OpenRouter uses openai.go with custom base_url
+ locale/ BCP 47 identity and CLDR plural categories
+ message/ ICU MessageFormat parser and structural comparison
+ policy/ Stable translation-policy hashing
+ state/ Versioned translation manifest
+ styleguide/ Style guide loader
+ tm/ JSONL translation memory
+ translate/ Translation orchestrator
+ validate/ Locale validation and diffing
```
-
+
## Сравнение с альтернативами
-| Функция | Internationalizer | i18next | Crowdin | Обычная LLM |
-|---------|------------------|---------|---------|-------------|
+| Возможность | Internationalizer | i18next | Crowdin | Обычные LLM |
+| --- | --- | --- | --- | --- |
| Перевод на базе LLM | Да | Нет | Частично | Да |
| Руководства по стилю для каждого языка | Да | Нет | Нет | Нет |
-| Применение глоссария | Да | Нет | Да | Нет |
+| Контроль соблюдения глоссария | Да | Нет | Да | Нет |
| Память переводов | Да | Нет | Да | Нет |
-| CLI / локальное выполнение | Да | Н/Д | Нет | Вручную |
-| Удобные для Git файлы | Да | Да | Частично | Вручную |
-| Отсутствие зависимости от SaaS | Да | Да | Нет | Зависит |
-| Открытый исходный код (AGPL-3.0) | Да | Да | Нет | Зависит |
-
+| CLI и локальный запуск | Да | Неприменимо | Нет | Вручную |
+| Удобство хранения файлов в Git | Да | Да | Частично | Вручную |
+| Без привязки к SaaS | Да | Да | Нет | Зависит от случая |
+| Открытый исходный код (AGPL-3.0) | Да | Да | Нет | Зависит от случая |
+
## Лицензия
-[AGPL-3.0](LICENSE)
+[AGPL-3.0](../../LICENSE)
+Сведения о зависимостях см. в [THIRD_PARTY_NOTICES.md](../../THIRD_PARTY_NOTICES.md).
+
## Участие в разработке
-Смотрите [CONTRIBUTING.md](CONTRIBUTING.md) для получения инструкций по настройке среды разработки и руководств. Все вклады требуют подписания DCO (DCO sign-off).
-
+Инструкции по настройке среды разработки и правила участия см. в [CONTRIBUTING.md](../../CONTRIBUTING.md). Для всех вкладов обязательно подписание DCO (DCO sign-off).
diff --git a/docs/i18n/sv.md b/docs/i18n/sv.md
index cb10884..b3968c8 100644
--- a/docs/i18n/sv.md
+++ b/docs/i18n/sv.md
@@ -9,14 +9,14 @@
AI-baserad pipeline för internationalisering av programvaruprojekt. Översätt, validera och hantera i18n-filer med LLM:er.
[](https://github.com/Tom-R-Main/Internationalizer/actions/workflows/ci.yml)
-[](LICENSE)
+[](../../LICENSE)
-العربية · বাংলা · Deutsch · Español · Français · हिन्दी · Indonesia · 日本語 · ਪੰਜਾਬੀ · Português · Русский · తెలుగు
ไทย · Українська · 粵語 · 简体中文 · 繁體中文
+العربية · বাংলা · Čeština · Dansk · Deutsch · Ελληνικά · Español · Suomi · Français · עברית · हिन्दी · Indonesia · Italiano · 日本語 · 한국어 · Bahasa Melayu
Nederlands · ਪੰਜਾਬੀ · Polski · Português · Română · Русский · Svenska · తెలుగు · ไทย · Türkçe · Українська · Tiếng Việt · 粵語 · 简体中文 · 繁體中文
---
-
+
## Varför Internationalizer?
De flesta i18n-verktyg är antingen runtime-bibliotek (i18next, react-intl) eller SaaS-plattformar för nyckelhantering (Crowdin, Lokalise). Inget av dem löser det faktiska översättningsproblemet på ett bra sätt:
@@ -25,13 +25,13 @@ De flesta i18n-verktyg är antingen runtime-bibliotek (i18next, react-intl) elle
- **API:er för maskinöversättning** (Google Translate, DeepL) ignorerar din terminologi, ton och dina gränssnittskonventioner
- **Generisk LLM-översättning** fungerar bättre, men utan ordlistor och stilguider får du inkonsekventa resultat
-Internationalizer är annorlunda. Det är en **CLI-pipeline** som kombinerar LLM-översättning med:
-
-- **Språkspecifika ordlistor** — säkerställ konsekvent terminologi i hela din app
-- **Språkspecifika stilguider** — kontrollera ton, formalitet, pluralisering och typografi
-- **Översättningsminne** — hoppa över oförändrade strängar och spara pengar på API-anrop
-- **Nyckelvalidering** — fånga upp saknade översättningar och interpoleringsfel innan de släpps
+Internationalizer skiljer sig från mängden. Det är en **CLI-pipeline** som kombinerar LLM-översättning med:
+- **Språkspecifika ordlistor** – säkerställ konsekvent terminologi i hela programmet
+- **Språkspecifika stilguider** – styr ton, formalitet, pluralisering och typografi
+- **Översättningsminne** – hoppa över oförändrade strängar och spara pengar på API-anrop
+- **Deterministisk validering** – fånga upp saknade eller extra nycklar, avvikelser i skyddad struktur, ordlisteproblem samt plural- eller ICU-fel innan de når produktion
+
## Installation
Installera från npm:
@@ -61,23 +61,27 @@ git clone https://github.com/Tom-R-Main/Internationalizer.git
cd Internationalizer
go build -o internationalizer ./cmd/internationalizer
```
-
+
## npm-paket
- Git-taggar och npm-paketversioner måste matcha, till exempel `v0.1.0` och `0.1.0`
- Rotpaketet `internationalizer` är beroende av plattformspaket som `internationalizer-darwin-arm64`
- npm-mål som stöds: macOS arm64/x64, Linux arm64/x64, Windows x64
-- CI-publicering kräver en GitHub-hemlighet med namnet `NPM_TOKEN`
-
+- Publicering via CI kräver en GitHub-hemlighet med namnet `NPM_TOKEN`
+
## Snabbstart
-1. Skapa en konfigurationsfil i din projektrot:
+1. Skapa en konfigurationsfil i projektroten:
```yaml
# .internationalizer.yml
source_locale: en
target_locales: [fr, de, es, ja]
-source_path: locales/en.json
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
llm:
provider: gemini
@@ -103,36 +107,64 @@ internationalizer translate --dry-run
internationalizer translate
```
-5. Validera alla språk:
+5. Validera alla språkmål:
```bash
internationalizer validate
```
-
+
## Kommandon
### `translate`
-Hitta saknade nycklar och översätt dem via en LLM.
+Hitta saknade eller inaktuella nycklar och översätt dem via en LLM.
```bash
internationalizer translate # translate all locales
internationalizer translate -l fr # translate French only
internationalizer translate --dry-run # preview without API calls
+internationalizer translate --adopt-existing # baseline existing translations without API calls
+internationalizer translate --refresh-policy # refresh prompt/style/model-stale entries
internationalizer translate --batch-size 20 # smaller batches
internationalizer translate --concurrency 2 # fewer parallel calls
```
+Översättningstillståndet rapporterar oberoende av varandra tillstånden saknad (missing), inaktuell källa (source-stale), inaktuell policy (policy-stale), aktuell (current) och manuellt redigerad (manually edited), så att en manuell redigering inte kan dölja en ändring i källan eller policyn. Värden med inaktuell policy rapporteras men översätts endast om med `--refresh-policy`. Manuellt redigerade värden skrivs aldrig över automatiskt. Använd `--adopt-existing` när manifestet introduceras för granskade översättningar eller när du uttryckligen vill acceptera en granskad manuell redigering som ny baslinje.
+
### `validate`
-Kontrollera alla språkfiler efter saknade nycklar, extra nycklar och interpoleringsfel.
+Kontrollera alla språkfiler mot källknippena. Standardvalideringen kontrollerar strukturell täckning (procentandelen obligatoriska målnycklar som finns), rapporterar extra nycklar som varningar och misslyckas vid saknade nycklar, bristande överensstämmelse i interpolering eller ogiltig struktur enligt ICU MessageFormat.
```bash
internationalizer validate # human-readable output
internationalizer validate --json # machine-readable JSON
internationalizer validate -q # exit code only
+internationalizer validate --strict # enforce translation quality rules
+internationalizer validate --require-state # require current manifest provenance
```
+`--strict` rapporterar även översättningstäckning. Ett språkligt värde som är identiskt med källan räknas som oöversatt, såvida inte ordlistan uttryckligen innehåller en post med exakt samma källa och mål för hela värdet. Flaggan `ignore_case` respekteras, men en ordlisteterm som ingår i ett längre värde utgör inget undantag. Strikt läge ger fel vid extra nycklar, källidentiska värden, ändrad struktur för interpolering/HTML/kod/Markdown-länkar, överträdelser mot ordlistan och konfigurerade pluralformer.
+
+`--require-state` verifierar varje målfil mot `.internationalizer.lock`. Kommandot misslyckas om en nyckel inte spåras, eller om dess registrerade källa, översättningspolicy eller målhash är inaktuell. Det kan kombineras med `--strict`.
+
+Rapporter i text- och JSON-format använder stabila identifieringskoder:
+
+| Kod | Betydelse |
+| --- | --- |
+| `missing_key` / `extra_key` | Nyckeluppsättningarna i källan och målet skiljer sig åt |
+| `blank_translation` | En icke-tom källa har ett tomt målvärde i strikt läge |
+| `source_identical` | Ett språkligt värde i strikt läge är fortfarande oöversatt |
+| `protected_structure_mismatch` | Strukturen för interpolering, HTML, kod eller länk har ändrats |
+| `glossary_violation` | Ingen godkänd målterm eller variant hittades |
+| `plural_form_missing` | En konfigurerad pluralform saknas för målspråket |
+| `icu_message_syntax` | Ett ICU-meddelande i källan eller målet är felaktigt utformat |
+| `icu_argument_mismatch` | ICU-argumentnamn, typer eller formateringsstilar skiljer sig åt |
+| `icu_selector_mismatch` | Selektorer skiljer sig åt eller en pluralkategori är ogiltig för målspråket |
+| `untracked` | Det finns ingen manifestpost för målet |
+| `source_stale` | Källinnehållet ändrades efter den registrerade översättningen |
+| `policy_stale` | Den genererade prompten eller modellinställningarna har ändrats |
+| `target_modified` | Målinnehållet skiljer sig från manifestposten |
+
### `detect`
Identifiera i18n-ramverket automatiskt och föreslå en konfiguration.
@@ -155,14 +187,14 @@ internationalizer glossary remove --locale fr --source "Dashboard"
### `tm`
-Hantera översättningsminne (JSONL-cache med tidigare översatta strängar).
+Hantera översättningsminnet (JSONL-cache med tidigare översatta strängar).
```bash
internationalizer tm stats # show record counts
internationalizer tm export # dump as JSON
internationalizer tm clear --force # delete all records
```
-
+
## Konfigurationsreferens
```yaml
@@ -172,10 +204,23 @@ internationalizer tm clear --force # delete all records
source_locale: en
# Languages to translate into (required)
-target_locales: [fr, de, es, ja, zh-CN, ar]
-
-# Path to the source locale file (required)
-source_path: locales/en.json
+target_locales: [fr, de, es, ja, yue, zh-CN, zh-TW, ar]
+
+# One or more source-to-target mappings (required).
+# {locale} is replaced with each configured target locale.
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
+ - id: docs
+ source: README.md
+ target: docs/i18n/{locale}.md
+ format: markdown
+
+# Backward compatibility: source_path still maps targets to sibling files
+# such as locales/fr.json. Prefer bundles for new projects.
+# source_path: locales/en.json
# LLM provider settings
llm:
@@ -184,7 +229,7 @@ llm:
# Model name defaults by provider:
# anthropic: claude-opus-5
- # openai: gpt-5.6-luna
+ # openai: gpt-5.6-luna (reasoning effort defaults to max)
# gemini: gemini-3.8-flash
# openrouter: deepseek/deepseek-v4-pro-0813
model: gemini-3.8-flash
@@ -195,6 +240,27 @@ llm:
# Base URL for OpenAI-compatible endpoints (optional)
# base_url: https://api.openai.com
+ # OpenAI GPT-5-series Responses API reasoning effort
+ # (default: max for the OpenAI provider)
+ reasoning_effort: max
+
+ # Optional LLM settings for individual target locales. An override using the
+ # global provider inherits unspecified global settings. A different provider
+ # uses that provider's defaults for unspecified settings.
+ locale_overrides:
+ yue:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-CN:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-TW:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+
# Keys per LLM call (default: 40)
batch_size: 40
@@ -209,11 +275,25 @@ glossary_dir: glossary
# Path to translation memory file (default: .internationalizer/tm.jsonl)
tm_path: .internationalizer/tm.jsonl
+
+# Versioned source, policy, target, and provenance state
+# (default: .internationalizer.lock; commit this file)
+manifest_path: .internationalizer.lock
+
+# Optional translation and strict-validation rules
+validation:
+ plural_style: i18next-v4 # generate and validate target-locale plural forms
```
+Språkidentifierare måste vara välformade BCP 47-taggar som `fr`, `pt-BR` eller `sr-Latn-RS`. Kanoniskt likvärdiga målspråk avvisas som dubbletter, och språkspecifika åsidosättningar av leverantör matchas mot kanoniskt likvärdig stavning. I exemplet ovan ärver språkmål utan en åsidosättning – däribland japanska – den globala Gemini-konfigurationen.
+
+Värden i ICU MessageFormat tolkas strukturellt. Enkla argument, `select`, `plural`, `selectordinal`, `number`, `date` och `time` stöds, inklusive nästlade meddelanden, pluralförskjutningar, selektorer med exakta tal samt `#`. Valideringen kontrollerar syntax, argumenttyper och formateringsstilar, pluralförskjutningar, identitet för select-grenar och CLDR-pluralkategorier för målspråket. Leverantörssvar som bryter mot dessa invarianter avvisas innan en språkfil eller en post i översättningsminnet skrivs.
+
+Med `i18next-v4` expanderas identifierade pluralfamiljer i källan under översättningen till målspråkets CLDR-kategorier. En kategori som bara finns i målet använder källfamiljens `_other`-värde som översättningsmall. Strikt validering kräver dessa målkategorier; kategorier som bara finns i källan är valfria för målspråk som inte använder dem.
+
## Stilguider
-Stilguider är Markdown-filer som injiceras i LLM-översättningsprompten. De styr ton, formalitet, typografi och andra språkspecifika konventioner.
+Stilguider är Markdown-filer som injiceras i prompten för LLM-översättning. De styr ton, formalitet, typografi och andra språkspecifika konventioner.
```
style-guides/
@@ -225,58 +305,64 @@ style-guides/
### Delade konventioner (`_conventions.md`)
-Definiera regler som gäller för alla språk: interpoleringssyntax, bevarande av HTML, konventioner för strängtyper (knappar vs. etiketter vs. fel) etc.
+Definiera regler som gäller för alla språk: interpoleringssyntax, bevarande av HTML, konventioner för strängtyper (knappar jämfört med etiketter och fel) osv.
### Språkspecifika guider (`{locale}.md`)
-Definiera språkspecifika regler: formalitetsregister (tu vs. vous), interpunktion (citattecken, omvända frågetecken), pluralformer, datum-/nummerformatering och en terminologiordlista.
+Definiera språkspecifika regler: formalitetsregister (tu jämfört med vous), interpunktion (citattecken, omvända frågetecken), pluralformer, datum- och talformatering samt en terminologiordlista.
-Se [`examples/react-app/style-guides/`](examples/react-app/style-guides/) för ett fungerande exempel.
+Stilguider är beständiga policyunderlag, inte genererade utdata. Internationalizer läser dem men skriver aldrig om dem. Deras innehåll hashas separat från ordlistan och promptkontraktet, så att en kodändring i programmet inte gör en översättning inaktuell. Om du redigerar en guide markeras det språket avsiktligt för policygranskning; ändringar i interna promptformuleringar gör det inte, såvida inte promptkontraktets version också ändras.
+Se [`examples/react-app/style-guides/`](../../examples/react-app/style-guides/) för ett fungerande exempel.
+
## Ordlisteformat
-Ordlistefiler är JSON-vektorer som lagras i `{glossary_dir}/{locale}.json`:
+Ordlistefiler är JSON-matriser som lagras i `{glossary_dir}/{locale}.json`:
```json
[
{
"source": "Dashboard",
"target": "Tableau de bord",
+ "variants": ["Panneau de contrôle"],
+ "enforcement": "error",
"ignore_case": false,
"whole_word": true
}
]
```
-Termer injiceras i LLM-prompten som en terminologitabell, vilket säkerställer konsekvent översättning av nyckeltermer i hela din applikation.
-
+`variants` listar andra godkända målformer. `enforcement` kan vara `error`, `warning` eller utelämnas för standardbeteendet fel. Termerna injiceras i LLM-prompten som en terminologitabell, vilket säkerställer konsekvent översättning i hela programmet. En exakt post som `{"source":"API","target":"API"}` undantar även det fullständiga källidentiska värdet från anmärkningar om oöversatta värden i strikt läge; den undantar inte ett längre värde som bara innehåller `API`.
+
## Översättningsminne
Översättningsminnet lagras som en JSONL-fil (en JSON-post per rad). Varje post innehåller:
-- Källnyckeln och värdet
-- Det översatta värdet
-- En SHA-256-hash av källvärdet
+- Knippe, nyckel, källvärde, översatt värde och kanoniskt målspråk
+- Hashar för källa, stilguide, ordlista, promptkontrakt och kombinerad policy
+- Leverantören och modellen som genererade översättningen
- En tidsstämpel
-Vid efterföljande körningar hämtas oförändrade strängar från TM-cachen utan att anropa LLM:en, vilket sparar både tid och API-kostnader. TM-filen är git-vänlig och kan checkas in tillsammans med dina språkfiler.
-
+Vid efterföljande körningar hämtas strängar med samma käll- och policyhashar från cachen utan att anropa LLM:en. Standardsökvägen ligger under den ignorerade katalogen `.internationalizer/`, så att den förblir en lokal cache. Sätt `tm_path` till en spårad plats om projektet avsiktligt delar översättningsminne. Det granskningsbara manifestet `.internationalizer.lock` versionshanteras separat.
+
## Format som stöds
-| Format | Filändelser | Läge |
+| Format | Filnamnstillägg | Läge |
|--------|-----------|------|
| JSON | `.json` | Nyckel-värde (nästlad, tillplattad med punktnotation) |
| YAML | `.yml`, `.yaml` | Nyckel-värde (bevarar kommentarer och ordning) |
-| Markdown | `.md`, `.mdx` | Översättning av hela dokument |
+| Markdown | `.md`, `.mdx` | Ingress och avsnitt på H2-nivå |
+Markdown-målfiler innehåller osynliga `internationalizer:unit`-kommentarer före H2-avsnitt. Dessa stabila markörer gör att Internationalizer kan lägga till, flytta eller redigera ett enskilt källavsnitt utan att översätta om orelaterade avsnitt. Befintliga omarkerade dokument får markörer vid nästa lyckade uppdatering.
+
## Identifiering av projekttyp
-`internationalizer detect` identifierar din i18n-uppsättning genom att kontrollera:
+`internationalizer detect` identifierar din i18n-konfiguration genom att kontrollera:
- Beroenden i `package.json` för react-i18next, next-intl eller vue-i18n
- Katalogstrukturer som matchar vanliga språkmönster
-- Filändelser och namngivningskonventioner
-
+- Filnamnstillägg och namngivningskonventioner
+
## Arkitektur
```
@@ -291,12 +377,16 @@ internal/
openai.go OpenAI / compatible backend
gemini.go Google Gemini via AI Studio backend
OpenRouter uses openai.go with custom base_url
+ locale/ BCP 47 identity and CLDR plural categories
+ message/ ICU MessageFormat parser and structural comparison
+ policy/ Stable translation-policy hashing
+ state/ Versioned translation manifest
styleguide/ Style guide loader
tm/ JSONL translation memory
translate/ Translation orchestrator
validate/ Locale validation and diffing
```
-
+
## Jämförelse med alternativ
| Funktion | Internationalizer | i18next | Crowdin | Generisk LLM |
@@ -305,16 +395,17 @@ internal/
| Språkspecifika stilguider | Ja | Nej | Nej | Nej |
| Upprätthållande av ordlista | Ja | Nej | Ja | Nej |
| Översättningsminne | Ja | Nej | Ja | Nej |
-| CLI / lokal körning | Ja | Ej tillämpligt | Nej | Manuellt |
-| Git-vänliga filer | Ja | Ja | Delvis | Manuellt |
+| CLI / lokal körning | Ja | Ej tillämpligt | Nej | Manuell |
+| Git-vänliga filer | Ja | Ja | Delvis | Manuell |
| Inget SaaS-beroende | Ja | Ja | Nej | Varierar |
| Öppen källkod (AGPL-3.0) | Ja | Ja | Nej | Varierar |
-
+
## Licens
-[AGPL-3.0](LICENSE)
+[AGPL-3.0](../../LICENSE)
+Se [THIRD_PARTY_NOTICES.md](../../THIRD_PARTY_NOTICES.md) för information om beroendens licenser.
+
## Bidra
-Se [CONTRIBUTING.md](CONTRIBUTING.md) för utvecklingsmiljö och riktlinjer. Alla bidrag kräver DCO-godkännande.
-
+Se [CONTRIBUTING.md](../../CONTRIBUTING.md) för utvecklingsmiljö och riktlinjer. Alla bidrag kräver DCO-godkännande.
diff --git a/docs/i18n/te.md b/docs/i18n/te.md
index 74cb466..d9686e3 100644
--- a/docs/i18n/te.md
+++ b/docs/i18n/te.md
@@ -9,23 +9,31 @@
సాఫ్ట్వేర్ ప్రాజెక్ట్ల కోసం AI-నేటివ్ ఇంటర్నేషనలైజేషన్ పైప్లైన్. LLMలను ఉపయోగించి i18n ఫైల్లను అనువదించండి, ధృవీకరించండి మరియు నిర్వహించండి.
[](https://github.com/Tom-R-Main/Internationalizer/actions/workflows/ci.yml)
-[](LICENSE)
+[](../../LICENSE)
+
+العربية · বাংলা · Čeština · Dansk · Deutsch · Ελληνικά · Español · Suomi · Français · עברית · हिन्दी · Indonesia · Italiano · 日本語 · 한국어 · Bahasa Melayu
Nederlands · ਪੰਜਾਬੀ · Polski · Português · Română · Русский · Svenska · తెలుగు · ไทย · Türkçe · Українська · Tiếng Việt · 粵語 · 简体中文 · 繁體中文
+
+
+---
+
+
## Internationalizer ఎందుకు?
-చాలా i18n టూల్స్ రన్టైమ్ లైబ్రరీలు (i18next, react-intl) లేదా కీ-మేనేజ్మెంట్ SaaS ప్లాట్ఫారమ్లు (Crowdin, Lokalise). వాటిలో ఏవీ అసలు అనువాద సమస్యను సరిగ్గా పరిష్కరించలేవు:
+చాలా i18n టూల్స్ రన్టైమ్ లైబ్రరీలు (i18next, react-intl) లేదా కీ-మేనేజ్మెంట్ SaaS ప్లాట్ఫారమ్లు (Crowdin, Lokalise). వాటిలో ఏవీ వాస్తవ అనువాద సమస్యను సమర్థవంతంగా పరిష్కరించలేవు:
-- **మాన్యువల్ అనువాదం** (Manual translation) కొన్ని భాషల కంటే ఎక్కువ స్కేల్ చేయబడదు
-- **మెషిన్ ట్రాన్స్లేషన్ APIలు** (Google Translate, DeepL) మీ పరిభాష, టోన్ మరియు UI కన్వెన్షన్లను విస్మరిస్తాయి
-- **సాధారణ LLM అనువాదం** మెరుగ్గా పనిచేస్తుంది, కానీ గ్లాసరీలు మరియు స్టైల్ గైడ్లు లేకుండా, మీకు అస్థిరమైన ఫలితాలు వస్తాయి
+- **మాన్యువల్ అనువాదం** కొన్ని భాషల కంటే ఎక్కువ విస్తరించలేదు
+- **మెషిన్ ట్రాన్స్లేషన్ APIలు** (Google Translate, DeepL) మీ పరిభాష, టోన్ మరియు UI కన్వెన్షన్లను పరిగణనలోకి తీసుకోవు
+- **సాధారణ LLM అనువాదం** మెరుగ్గానే పనిచేస్తుంది, కానీ గ్లాసరీలు మరియు స్టైల్ గైడ్లు లేకపోతే అస్థిరమైన ఫలితాలు వస్తాయి
-Internationalizer భిన్నమైనది. ఇది LLM అనువాదాన్ని వీటితో కలిపే ఒక **CLI పైప్లైన్**:
+Internationalizer భిన్నమైనది. ఇది LLM అనువాదాన్ని వీటితో అనుసంధానించే ఒక **CLI పైప్లైన్**:
-- **భాషల వారీగా గ్లాసరీలు** — మీ యాప్ అంతటా స్థిరమైన పరిభాషను అమలు చేస్తుంది
-- **భాషల వారీగా స్టైల్ గైడ్లు** — టోన్, ఫార్మాలిటీ, బహువచనాలు మరియు టైపోగ్రఫీని నియంత్రిస్తుంది
-- **ట్రాన్స్లేషన్ మెమరీ** — మార్చబడని స్ట్రింగ్లను దాటవేస్తుంది, API కాల్లలో డబ్బును ఆదా చేస్తుంది
-- **కీ వాలిడేషన్** — షిప్ చేయడానికి ముందే తప్పిపోయిన అనువాదాలు మరియు ఇంటర్పోలేషన్ మిస్మ్యాచ్లను పట్టుకుంటుంది
+- **భాషల వారీగా గ్లాసరీలు** — మీ యాప్ అంతటా స్థిరమైన సాంకేతిక పరిభాషను అమలు చేయండి
+- **భాషల వారీగా స్టైల్ గైడ్లు** — టోన్, ఫార్మాలిటీ, బహువచనాలు మరియు టైపోగ్రఫీని నియంత్రించండి
+- **ట్రాన్స్లేషన్ మెమరీ** — మార్పులేని స్ట్రింగ్లను దాటవేయండి, తద్వారా API కాల్ ఖర్చులను ఆదా చేయండి
+- **డిటర్మినిస్టిక్ వాలిడేషన్** — కోడ్ విడుదల కావడానికి ముందే తప్పిపోయిన లేదా అదనపు కీలు, ప్రొటెక్టెడ్-స్ట్రక్చర్ మార్పులు, గ్లాసరీ సమస్యలు మరియు బహువచన లేదా ICU లోపాలను గుర్తించండి
+
## ఇన్స్టాలేషన్
npm నుండి ఇన్స్టాల్ చేయండి:
@@ -34,7 +42,7 @@ npm నుండి ఇన్స్టాల్ చేయండి:
npm install -g internationalizer
```
-లేదా గ్లోబల్ ఇన్స్టాల్ లేకుండా రన్ చేయండి:
+లేదా గ్లోబల్ ఇన్స్టాలేషన్ లేకుండా నేరుగా రన్ చేయండి:
```bash
npx internationalizer --help
@@ -48,7 +56,7 @@ Go తో ఇన్స్టాల్ చేయండి:
go install github.com/Tom-R-Main/Internationalizer/cmd/internationalizer@latest
```
-లేదా సోర్స్ నుండి బిల్డ్ చేయండి:
+లేదా సోర్స్ కోడ్ నుండి బిల్డ్ చేయండి:
```bash
git clone https://github.com/Tom-R-Main/Internationalizer.git
@@ -56,26 +64,32 @@ cd Internationalizer
go build -o internationalizer ./cmd/internationalizer
```
+
## npm ప్యాకేజీలు
-- Git ట్యాగ్లు మరియు npm ప్యాకేజీ వెర్షన్లు తప్పనిసరిగా సరిపోలాలి, ఉదాహరణకు `v0.1.0` మరియు `0.1.0`
+- Git ట్యాగ్లు మరియు npm ప్యాకేజీ వెర్షన్లు ఖచ్చితంగా సరిపోలాలి, ఉదాహరణకు `v0.1.0` మరియు `0.1.0`
- రూట్ `internationalizer` ప్యాకేజీ `internationalizer-darwin-arm64` వంటి ప్లాట్ఫారమ్ ప్యాకేజీలపై ఆధారపడి ఉంటుంది
-- సపోర్ట్ చేసే npm టార్గెట్లు: macOS arm64/x64, Linux arm64/x64, Windows x64
+- మద్దతు ఉన్న npm టార్గెట్లు: macOS arm64/x64, Linux arm64/x64, Windows x64
- CI పబ్లిషింగ్ కోసం `NPM_TOKEN` పేరుతో ఒక GitHub సీక్రెట్ అవసరం
-## క్విక్ స్టార్ట్
+
+## త్వరిత ప్రారంభం
-1. మీ ప్రాజెక్ట్ రూట్లో కాన్ఫిగ్ ఫైల్ను సృష్టించండి:
+1. ప్రాజెక్ట్ రూట్ డైరెక్టరీలో కాన్ఫిగరేషన్ ఫైల్ను సృష్టించండి:
```yaml
# .internationalizer.yml
source_locale: en
target_locales: [fr, de, es, ja]
-source_path: locales/en.json
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
llm:
provider: gemini
- model: gemini-3.1-pro-preview
+ model: gemini-3.8-flash
api_key_env: GOOGLE_AI_STUDIO_API_KEY
```
@@ -97,49 +111,78 @@ internationalizer translate --dry-run
internationalizer translate
```
-5. అన్ని లొకేల్లను వాలిడేట్ చేయండి:
+5. అన్ని లొకేల్లను ధృవీకరించండి:
```bash
internationalizer validate
```
-## కమాండ్లు
+
+## ఆదేశాలు
### `translate`
-తప్పిపోయిన కీలను కనుగొని, వాటిని LLM ద్వారా అనువదించండి.
+తప్పిపోయిన లేదా పాతబడిన కీలను గుర్తించి, LLM ద్వారా వాటిని అనువదించండి.
```bash
-internationalizer translate # అన్ని లొకేల్లను అనువదించండి
-internationalizer translate -l fr # ఫ్రెంచ్ మాత్రమే అనువదించండి
-internationalizer translate --dry-run # API కాల్స్ లేకుండా ప్రివ్యూ చూడండి
-internationalizer translate --batch-size 20 # చిన్న బ్యాచ్లు
-internationalizer translate --concurrency 2 # తక్కువ ప్యారలల్ కాల్స్
+internationalizer translate # translate all locales
+internationalizer translate -l fr # translate French only
+internationalizer translate --dry-run # preview without API calls
+internationalizer translate --adopt-existing # baseline existing translations without API calls
+internationalizer translate --refresh-policy # refresh prompt/style/model-stale entries
+internationalizer translate --batch-size 20 # smaller batches
+internationalizer translate --concurrency 2 # fewer parallel calls
```
+అనువాద స్థితి మిస్సింగ్, సోర్స్-స్టేల్, పాలసీ-స్టేల్, కరెంట్ మరియు మాన్యువల్గా ఎడిట్ చేయబడిన స్థితులను వేర్వేరుగా నివేదిస్తుంది; కనుక మాన్యువల్ ఎడిట్ సోర్స్ లేదా పాలసీ మార్పును దాచలేదు. పాలసీ-స్టేల్ విలువలు నివేదించబడతాయి, కానీ కేవలం `--refresh-policy` ఉపయోగించినప్పుడు మాత్రమే తిరిగి అనువదించబడతాయి. మాన్యువల్గా ఎడిట్ చేయబడిన విలువలు ఎప్పటికీ ఆటోమేటిక్గా ఓవర్రైట్ కావు. సమీక్షించిన అనువాదాలకు మేనిఫెస్ట్ను పరిచయం చేస్తున్నప్పుడు లేదా సమీక్షించిన మాన్యువల్ ఎడిట్ను కొత్త బేస్లైన్గా స్పష్టంగా ఆమోదించేటప్పుడు `--adopt-existing` ను ఉపయోగించండి.
+
### `validate`
-తప్పిపోయిన కీలు, అదనపు కీలు మరియు ఇంటర్పోలేషన్ మిస్మ్యాచ్ల కోసం అన్ని లొకేల్ ఫైల్లను తనిఖీ చేయండి.
+సోర్స్ బండిల్స్తో పోల్చి అన్ని లొకేల్ ఫైల్లను తనిఖీ చేయండి. డిఫాల్ట్ ధృవీకరణ స్ట్రక్చరల్ కవరేజీని (అవసరమైన టార్గెట్ కీలు ఎంత శాతం ఉన్నాయో) తనిఖీ చేస్తుంది, అదనపు కీలను హెచ్చరికలుగా నివేదిస్తుంది మరియు తప్పిపోయిన కీలు, ఇంటర్పోలేషన్ అసమతుల్యతలు లేదా చెల్లని ICU MessageFormat నిర్మాణం ఉన్నప్పుడు విఫలమవుతుంది.
```bash
-internationalizer validate # చదవగలిగే అవుట్పుట్
-internationalizer validate --json # మెషిన్ చదవగలిగే JSON
-internationalizer validate -q # ఎగ్జిట్ కోడ్ మాత్రమే
+internationalizer validate # human-readable output
+internationalizer validate --json # machine-readable JSON
+internationalizer validate -q # exit code only
+internationalizer validate --strict # enforce translation quality rules
+internationalizer validate --require-state # require current manifest provenance
```
+`--strict` అనువదించబడిన కవరేజీని కూడా నివేదిస్తుంది. సోర్స్తో సమానంగా ఉండే భాషాపరమైన విలువ అనువదించబడనట్లే పరిగణించబడుతుంది; అయితే మొత్తం విలువకు ఖచ్చితమైన అదే-సోర్స్, అదే-టార్గెట్ ఎంట్రీ గ్లాసరీలో స్పష్టంగా ఉంటే తప్ప మినహాయింపు ఉండదు; `ignore_case` గౌరవించబడుతుంది, కానీ పెద్ద విలువలో భాగమైన ఒక గ్లాసరీ పదానికి మినహాయింపు వర్తించదు. అదనపు కీలు, సోర్స్తో సమానమైన విలువలు, మారిన ఇంటర్పోలేషన్/HTML/కోడ్/మార్క్డౌన్-లింక్ నిర్మాణం, గ్లాసరీ ఉల్లంఘనలు మరియు కాన్ఫిగర్ చేసిన బహువచన రూపాల లోపాలపై స్ట్రిక్ట్ మోడ్ విఫలమవుతుంది.
+
+`--require-state` ప్రతి టార్గెట్ను `.internationalizer.lock` ఆధారంగా ధృవీకరిస్తుంది. ఒక కీ అన్ట్రాక్ చేయబడినా, లేదా దాని రికార్డ్ చేసిన సోర్స్, అనువాద విధానం (పాలసీ) లేదా టార్గెట్ హాష్ పాతబడినా (స్టేల్ అయినా) ఇది విఫలమవుతుంది. దీనిని `--strict` తో కలిపి కూడా ఉపయోగించవచ్చు.
+
+మానవ రీడబుల్ మరియు JSON నివేదికలు స్థిరమైన ఫైండింగ్ కోడ్లను ఉపయోగిస్తాయి:
+
+| కోడ్ | అర్థం |
+| --- | --- |
+| `missing_key` / `extra_key` | సోర్స్ మరియు టార్గెట్ కీ సెట్లు భిన్నంగా ఉన్నాయి |
+| `blank_translation` | ఖాళీ లేని సోర్స్కు స్ట్రిక్ట్-మోడ్లో ఖాళీ టార్గెట్ ఉంది |
+| `source_identical` | స్ట్రిక్ట్-మోడ్లో భాషాపరమైన విలువ అనువదించబడకుండా మిగిలిపోయింది |
+| `protected_structure_mismatch` | ఇంటర్పోలేషన్, HTML, కోడ్ లేదా లింక్ నిర్మాణం మారింది |
+| `glossary_violation` | ఆమోదించబడిన టార్గెట్ పదం లేదా వేరియంట్ కనుగొనబడలేదు |
+| `plural_form_missing` | కాన్ఫిగర్ చేసిన లొకేల్ బహువచన రూపం లేదు |
+| `icu_message_syntax` | సోర్స్ లేదా టార్గెట్ ICU సందేశం సరిగ్గా లేదు |
+| `icu_argument_mismatch` | ICU ఆర్గ్యుమెంట్ పేర్లు, రకాలు లేదా ఫార్మాటర్ శైలులు భిన్నంగా ఉన్నాయి |
+| `icu_selector_mismatch` | సెలెక్టర్లు భిన్నంగా ఉన్నాయి లేదా టార్గెట్ లొకేల్కు బహువచన కేటగిరీ చెల్లదు |
+| `untracked` | టార్గెట్ కోసం ఎలాంటి మేనిఫెస్ట్ రికార్డ్ లేదు |
+| `source_stale` | రికార్డ్ చేసిన అనువాదం తర్వాత సోర్స్ కంటెంట్ మారింది |
+| `policy_stale` | రూపొందించిన ప్రాంప్ట్ లేదా మోడల్ సెట్టింగ్లు మారాయి |
+| `target_modified` | టార్గెట్ కంటెంట్ మేనిఫెస్ట్ రికార్డుతో సరిపోలడం లేదు |
+
### `detect`
-i18n ఫ్రేమ్వర్క్ను ఆటో-డిటెక్ట్ చేసి, కాన్ఫిగరేషన్ను సూచించండి.
+i18n ఫ్రేమ్వర్క్ను ఆటోమేటిక్గా గుర్తించి, అనుకూలమైన కాన్ఫిగరేషన్ను సూచించండి.
```bash
internationalizer detect
```
-సపోర్ట్ చేసేవి: react-i18next, next-intl, vue-i18n, vanilla JSON, markdown docs.
+మద్దతు ఉన్నవి: react-i18next, next-intl, vue-i18n, సాధారణ JSON, మార్క్డౌన్ డాక్యుమెంట్లు.
### `glossary`
-అనువాద సమయంలో అమలు చేయబడే భాషల వారీగా గ్లాసరీ పదాలను నిర్వహించండి.
+అనువాద సమయంలో అమలు చేయబడే భాషల వారీగా గ్లాసరీ నిబంధనలను నిర్వహించండి.
```bash
internationalizer glossary list --locale fr
@@ -152,163 +195,232 @@ internationalizer glossary remove --locale fr --source "Dashboard"
ట్రాన్స్లేషన్ మెమరీని నిర్వహించండి (గతంలో అనువదించబడిన స్ట్రింగ్ల JSONL కాష్).
```bash
-internationalizer tm stats # రికార్డ్ కౌంట్లను చూపించు
-internationalizer tm export # JSON గా డంప్ చేయి
-internationalizer tm clear --force # అన్ని రికార్డులను తొలగించు
+internationalizer tm stats # show record counts
+internationalizer tm export # dump as JSON
+internationalizer tm clear --force # delete all records
```
+
## కాన్ఫిగరేషన్ రిఫరెన్స్
```yaml
# .internationalizer.yml
-# సోర్స్ భాష (డిఫాల్ట్: en)
+# Source language (default: en)
source_locale: en
-# అనువదించాల్సిన భాషలు (తప్పనిసరి)
-target_locales: [fr, de, es, ja, zh-CN, ar]
-
-# సోర్స్ లొకేల్ ఫైల్ పాత్ (తప్పనిసరి)
-source_path: locales/en.json
-
-# LLM ప్రొవైడర్ సెట్టింగ్లు
+# Languages to translate into (required)
+target_locales: [fr, de, es, ja, yue, zh-CN, zh-TW, ar]
+
+# One or more source-to-target mappings (required).
+# {locale} is replaced with each configured target locale.
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
+ - id: docs
+ source: README.md
+ target: docs/i18n/{locale}.md
+ format: markdown
+
+# Backward compatibility: source_path still maps targets to sibling files
+# such as locales/fr.json. Prefer bundles for new projects.
+# source_path: locales/en.json
+
+# LLM provider settings
llm:
- # ప్రొవైడర్: "anthropic", "openai", "gemini", లేదా "openrouter" (డిఫాల్ట్: gemini)
+ # Provider: "anthropic", "openai", "gemini", or "openrouter" (default: gemini)
provider: gemini
- # ప్రొవైడర్ ద్వారా మోడల్ పేరు డిఫాల్ట్లు:
- # anthropic: claude-sonnet-4-6
- # openai: gpt-5.4
- # gemini: gemini-3.1-pro-preview
- # openrouter: google/gemini-3-flash-preview
- model: gemini-3.1-pro-preview
+ # Model name defaults by provider:
+ # anthropic: claude-opus-5
+ # openai: gpt-5.6-luna (reasoning effort defaults to max)
+ # gemini: gemini-3.8-flash
+ # openrouter: deepseek/deepseek-v4-pro-0813
+ model: gemini-3.8-flash
- # API కీని కలిగి ఉన్న ఎన్విరాన్మెంట్ వేరియబుల్
+ # Environment variable containing the API key
api_key_env: GOOGLE_AI_STUDIO_API_KEY
- # OpenAI-అనుకూల ఎండ్పాయింట్ల కోసం బేస్ URL (ఆప్షనల్)
+ # Base URL for OpenAI-compatible endpoints (optional)
# base_url: https://api.openai.com
-# ప్రతి LLM కాల్కు కీలు (డిఫాల్ట్: 40)
+ # OpenAI GPT-5-series Responses API reasoning effort
+ # (default: max for the OpenAI provider)
+ reasoning_effort: max
+
+ # Optional LLM settings for individual target locales. An override using the
+ # global provider inherits unspecified global settings. A different provider
+ # uses that provider's defaults for unspecified settings.
+ locale_overrides:
+ yue:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-CN:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-TW:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+
+# Keys per LLM call (default: 40)
batch_size: 40
-# ప్యారలల్ LLM కాల్స్ (డిఫాల్ట్: 4)
+# Parallel LLM calls (default: 4)
concurrency: 4
-# లొకేల్ వారీగా స్టైల్ గైడ్ మార్క్డౌన్ ఫైల్లను కలిగి ఉన్న డైరెక్టరీ (డిఫాల్ట్: style-guides)
+# Directory containing per-locale style guide Markdown files (default: style-guides)
style_guides_dir: style-guides
-# లొకేల్ వారీగా గ్లాసరీ JSON ఫైల్లను కలిగి ఉన్న డైరెక్టరీ (డిఫాల్ట్: glossary)
+# Directory containing per-locale glossary JSON files (default: glossary)
glossary_dir: glossary
-# ట్రాన్స్లేషన్ మెమరీ ఫైల్ పాత్ (డిఫాల్ట్: .internationalizer/tm.jsonl)
+# Path to translation memory file (default: .internationalizer/tm.jsonl)
tm_path: .internationalizer/tm.jsonl
+
+# Versioned source, policy, target, and provenance state
+# (default: .internationalizer.lock; commit this file)
+manifest_path: .internationalizer.lock
+
+# Optional translation and strict-validation rules
+validation:
+ plural_style: i18next-v4 # generate and validate target-locale plural forms
```
+లొకేల్ ఐడెంటిఫైయర్లు `fr`, `pt-BR`, లేదా `sr-Latn-RS` వంటి సరైన రూపంలో ఉన్న BCP 47 ట్యాగ్లై ఉండాలి. ప్రామాణిక సమానమైన (canonical-equivalent) టార్గెట్ లొకేల్లు డూప్లికేట్లుగా తిరస్కరించబడతాయి, అలాగే లొకేల్-నిర్దిష్ట ప్రొవైడర్ ఓవర్రైడ్లు ప్రామాణిక సమానమైన స్పెల్లింగ్తో సరిపోలుతాయి. పై ఉదాహరణలో, జపనీస్తో సహా ఎలాంటి ఓవర్రైడ్ లేని లొకేల్లు గ్లోబల్ Gemini కాన్ఫిగరేషన్ను పొందుతాయి.
+
+ICU MessageFormat విలువలు నిర్మాణాత్మకంగా అన్వయించబడతాయి. సాధారణ ఆర్గ్యుమెంట్లు, `select`, `plural`, `selectordinal`, `number`, `date` మరియు `time` లకు మద్దతు ఉంది; వీటిలో నెస్టెడ్ సందేశాలు, బహువచన ఆఫ్సెట్లు, ఖచ్చితమైన సంఖ్య సెలెక్టర్లు మరియు `#` లు కూడా ఉంటాయి. సింటాక్స్, ఆర్గ్యుమెంట్ రకాలు మరియు ఫార్మాటర్ శైలులు, బహువచన ఆఫ్సెట్లు, సెలెక్ట్ బ్రాంచ్ గుర్తింపు మరియు టార్గెట్ లొకేల్ CLDR బహువచన కేటగిరీలను వాలిడేషన్ తనిఖీ చేస్తుంది. ఈ నియమాలను ఉల్లంఘించే ప్రొవైడర్ అవుట్పుట్, లొకేల్ ఫైల్ లేదా ట్రాన్స్లేషన్ మెమరీ రికార్డ్ రాయబడక ముందే తిరస్కరించబడుతుంది.
+
+`i18next-v4` తో, గుర్తించబడిన సోర్స్ బహువచన కుటుంబాలు అనువాద సమయంలో టార్గెట్ లొకేల్ యొక్క CLDR కేటగిరీలకు విస్తరించబడతాయి. టార్గెట్కు మాత్రమే చెందిన కేటగిరీ సోర్స్ కుటుంబం యొక్క `_other` విలువను తన అనువాద టెంప్లేట్గా ఉపయోగిస్తుంది. స్ట్రిక్ట్ వాలిడేషన్కు ఆ టార్గెట్ కేటగిరీలు తప్పనిసరిగా అవసరం; వాటిని ఉపయోగించని టార్గెట్ లొకేల్లకు సోర్స్కు మాత్రమే చెందిన కేటగిరీలు ఆప్షనల్.
+
+
## స్టైల్ గైడ్లు
-స్టైల్ గైడ్లు అనేవి LLM అనువాద ప్రాంప్ట్లోకి ఇంజెక్ట్ చేయబడే మార్క్డౌన్ ఫైల్లు. ఇవి టోన్, ఫార్మాలిటీ, టైపోగ్రఫీ మరియు ఇతర భాషా-నిర్దిష్ట కన్వెన్షన్లను నియంత్రిస్తాయి.
+స్టైల్ గైడ్లు అనేవి LLM అనువాద ప్రాంప్ట్లోకి చేర్చబడే మార్క్డౌన్ ఫైల్లు. ఇవి టోన్, ఫార్మాలిటీ, టైపోగ్రఫీ మరియు ఇతర భాషా-నిర్దిష్ట నియమాలను నియంత్రిస్తాయి.
```
style-guides/
- _conventions.md # అన్ని భాషల కోసం షేర్ చేయబడిన నియమాలు
- fr.md # ఫ్రెంచ్-నిర్దిష్ట నియమాలు
- ja.md # జపనీస్-నిర్దిష్ట నియమాలు
- ar.md # అరబిక్-నిర్దిష్ట నియమాలు
+ _conventions.md # shared rules for all languages
+ fr.md # French-specific rules
+ ja.md # Japanese-specific rules
+ ar.md # Arabic-specific rules
```
-### షేర్ చేయబడిన కన్వెన్షన్లు (`_conventions.md`)
+### ఉమ్మడి నియమాలు (`_conventions.md`)
-అన్ని భాషలకు వర్తించే నియమాలను నిర్వచించండి: ఇంటర్పోలేషన్ సింటాక్స్, HTML ప్రిజర్వేషన్, స్ట్రింగ్ టైప్ కన్వెన్షన్లు (బటన్లు vs. లేబుల్లు vs. ఎర్రర్లు) మొదలైనవి.
+అన్ని భాషలకు వర్తించే నియమాలను నిర్వచించండి: ఇంటర్పోలేషన్ సింటాక్స్, HTML పరిరక్షణ, స్ట్రింగ్ రకం కన్వెన్షన్లు (బటన్లు vs. లేబుల్లు vs. లోపాలు) మొదలైనవి.
-### భాషల వారీగా గైడ్లు (`{locale}.md`)
+### భాషల వారీ గైడ్లు (`{locale}.md`)
-భాషా-నిర్దిష్ట నియమాలను నిర్వచించండి: ఫార్మాలిటీ రిజిస్టర్ (tu vs. vous), విరామచిహ్నాలు (guillemets, inverted question marks), బహువచన రూపాలు, తేదీ/సంఖ్య ఫార్మాటింగ్ మరియు పరిభాష గ్లాసరీ.
+భాషా-నిర్దిష్ట నియమాలను నిర్వచించండి: మర్యాదపూర్వక సంబోధన (tu vs. vous), విరామచిహ్నాలు (guillemets, ఇన్వర్టెడ్ ప్రశ్నార్థకాలు), బహువచన రూపాలు, తేదీ/సంఖ్య ఫార్మాటింగ్ మరియు పరిభాష గ్లాసరీ.
-పనిచేసే ఉదాహరణ కోసం [`examples/react-app/style-guides/`](examples/react-app/style-guides/) చూడండి.
+స్టైల్ గైడ్లు అనేవి శాశ్వతమైన పాలసీ ఇన్పుట్లు, రూపొందించబడిన అవుట్పుట్ కాదు. Internationalizer వాటిని చదువుతుంది కానీ ఎప్పటికీ తిరిగి రాయదు. వాటి కంటెంట్ గ్లాసరీ మరియు ప్రాంప్ట్ కాంట్రాక్ట్ నుండి విడిగా హాష్ చేయబడుతుంది, కనుక అప్లికేషన్ కోడ్ మార్పు అనువాదాన్ని పాతబడేలా (స్టేల్ అయ్యేలా) చేయదు. ఒక గైడ్ను ఎడిట్ చేసినప్పుడు ఆ లొకేల్ ఉద్దేశపూర్వకంగా పాలసీ సమీక్ష కోసం గుర్తించబడుతుంది; ప్రాంప్ట్ కాంట్రాక్ట్ వెర్షన్ మారితే తప్ప అంతర్గత ప్రాంప్ట్ పదాల మార్పు దానిని ప్రభావితం చేయదు.
+పనిచేసే ఉదాహరణ కోసం [`examples/react-app/style-guides/`](../../examples/react-app/style-guides/) చూడండి.
+
+
## గ్లాసరీ ఫార్మాట్
-గ్లాసరీ ఫైల్లు `{glossary_dir}/{locale}.json` లో నిల్వ చేయబడిన JSON శ్రేణులు:
+గ్లాసరీ ఫైల్లు `{glossary_dir}/{locale}.json` లో భద్రపరచబడే JSON శ్రేణులు (arrays):
```json
[
{
"source": "Dashboard",
"target": "Tableau de bord",
+ "variants": ["Panneau de contrôle"],
+ "enforcement": "error",
"ignore_case": false,
"whole_word": true
}
]
```
-పదాలు LLM ప్రాంప్ట్లోకి టెర్మినాలజీ టేబుల్గా ఇంజెక్ట్ చేయబడతాయి, ఇది మీ అప్లికేషన్ అంతటా కీలక పదాల స్థిరమైన అనువాదాన్ని నిర్ధారిస్తుంది.
+`variants` అనేది ఆమోదించబడిన ఇతర టార్గెట్ రూపాలను సూచిస్తుంది. `enforcement` అనేది `error`, `warning` కావచ్చు లేదా డిఫాల్ట్ ఎర్రర్ ప్రవర్తన కోసం వదిలివేయబడవచ్చు. పదాలు LLM ప్రాంప్ట్లోకి టెర్మినాలజీ పట్టికగా ఇంజెక్ట్ చేయబడతాయి, ఇది మీ అప్లికేషన్ అంతటా స్థిరమైన అనువాదాన్ని నిర్ధారిస్తుంది. `{"source":"API","target":"API"}` వంటి ఖచ్చితమైన ఎంట్రీ, పూర్తిగా సోర్స్తో సమానమైన ఆ విలువను స్ట్రిక్ట్ అన్ట్రాన్స్లేటెడ్-విలువ ఫైండింగ్ల నుండి మినహాయిస్తుంది; అయితే కేవలం `API` ఉన్న పెద్ద విలువకు ఈ మినహాయింపు వర్తించదు.
+
## ట్రాన్స్లేషన్ మెమరీ
-ట్రాన్స్లేషన్ మెమరీ JSONL ఫైల్గా నిల్వ చేయబడుతుంది (లైన్కు ఒక JSON రికార్డ్). ప్రతి రికార్డ్ వీటిని కలిగి ఉంటుంది:
+ట్రాన్స్లేషన్ మెమరీ ఒక JSONL ఫైల్గా భద్రపరచబడుతుంది (ఒక్కో లైన్కు ఒక JSON రికార్డు). ప్రతి రికార్డు వీటిని కలిగి ఉంటుంది:
-- సోర్స్ కీ మరియు వాల్యూ
-- అనువదించబడిన వాల్యూ
-- సోర్స్ వాల్యూ యొక్క SHA-256 హాష్
+- బండిల్, కీ, సోర్స్ విలువ, అనువదించబడిన విలువ మరియు ప్రామాణిక టార్గెట్ లొకేల్
+- సోర్స్, స్టైల్ గైడ్, గ్లాసరీ, ప్రాంప్ట్-కాంట్రాక్ట్ మరియు మిశ్రమ పాలసీ హాష్లు
+- అనువాదాన్ని రూపొందించిన ప్రొవైడర్ మరియు మోడల్
- ఒక టైమ్స్టాంప్
-తదుపరి రన్లలో, మార్చబడని స్ట్రింగ్లు LLMని కాల్ చేయకుండానే TM కాష్ నుండి అందించబడతాయి, దీని వలన సమయం మరియు API ఖర్చులు రెండూ ఆదా అవుతాయి. TM ఫైల్ git-ఫ్రెండ్లీ మరియు మీ లొకేల్ ఫైల్లతో పాటు కమిట్ చేయవచ్చు.
+తదుపరి రన్లలో, అదే సోర్స్ మరియు పాలసీ హాష్లను కలిగి ఉన్న స్ట్రింగ్లు LLMకి కాల్ చేయకుండానే కాష్ నుండి అందించబడతాయి. డిఫాల్ట్ పాత్ ఇగ్నోర్ చేయబడిన `.internationalizer/` డైరెక్టరీలో ఉంటుంది, కాబట్టి ఇది లోకల్ కాష్గానే ఉంటుంది. మీ ప్రాజెక్ట్ ఉద్దేశపూర్వకంగా ట్రాన్స్లేషన్ మెమరీని షేర్ చేసుకోవాలనుకుంటే `tm_path` ను ట్రాక్ చేయబడే ప్రదేశానికి సెట్ చేయండి. సమీక్షించదగిన `.internationalizer.lock` మేనిఫెస్ట్ విడిగా వెర్షన్ చేయబడుతుంది.
-## సపోర్ట్ చేసే ఫార్మాట్లు
+
+## మద్దతు ఉన్న ఫార్మాట్లు
| ఫార్మాట్ | ఎక్స్టెన్షన్లు | మోడ్ |
|--------|-----------|------|
-| JSON | `.json` | కీ-వాల్యూ (నెస్టెడ్, డాట్-నొటేషన్ ఫ్లాటెన్డ్) |
-| YAML | `.yml`, `.yaml` | కీ-వాల్యూ (కామెంట్లు మరియు ఆర్డరింగ్ను సంరక్షిస్తుంది) |
-| Markdown | `.md`, `.mdx` | పూర్తి-డాక్యుమెంట్ అనువాదం |
+| JSON | `.json` | కీ-విలువ (నెస్టెడ్, డాట్-నొటేషన్ ఫ్లాటెన్డ్) |
+| YAML | `.yml`, `.yaml` | కీ-విలువ (కామెంట్లు మరియు క్రమాన్ని పరిరక్షిస్తుంది) |
+| Markdown | `.md`, `.mdx` | పీఠిక మరియు H2-స్థాయి విభాగాలు |
+
+మార్క్డౌన్ టార్గెట్లు H2 విభాగాల కంటే ముందు అదృశ్య `internationalizer:unit` కామెంట్లను కలిగి ఉంటాయి. ఈ స్థిరమైన మార్కర్లు ఇతర విభాగాలను తిరిగి అనువదించాల్సిన అవసరం లేకుండానే ఏదైనా ఒక సోర్స్ విభాగాన్ని జోడించడానికి, తరలించడానికి లేదా సవరించడానికి Internationalizer కు సహాయపడతాయి. మార్కర్లు లేని ఇప్పటికే ఉన్న డాక్యుమెంట్లు వాటి తదుపరి విజయవంతమైన అప్డేట్లో ఈ మార్కర్లను పొందుతాయి.
-## ప్రాజెక్ట్ టైప్ డిటెక్షన్
+
+## ప్రాజెక్ట్ రకం గుర్తింపు
`internationalizer detect` వీటిని తనిఖీ చేయడం ద్వారా మీ i18n సెటప్ను గుర్తిస్తుంది:
- react-i18next, next-intl, లేదా vue-i18n కోసం `package.json` డిపెండెన్సీలు
-- సాధారణ లొకేల్ ప్యాటర్న్లకు సరిపోలే డైరెక్టరీ స్ట్రక్చర్లు
+- సాధారణ లొకేల్ ప్యాటర్న్లకు సరిపోలే డైరెక్టరీ నిర్మాణాలు
- ఫైల్ ఎక్స్టెన్షన్లు మరియు నేమింగ్ కన్వెన్షన్లు
+
## ఆర్కిటెక్చర్
```
-cmd/internationalizer/ CLI ఎంట్రీ పాయింట్ మరియు కమాండ్ డెఫినిషన్స్
+cmd/internationalizer/ CLI entry point and command definitions
internal/
- config/ డిఫాల్ట్లతో YAML కాన్ఫిగ్ లోడింగ్
- detect/ ప్రాజెక్ట్ టైప్ ఆటో-డిటెక్షన్
- formats/ ఫార్మాట్ పార్సర్లు (JSON, YAML, Markdown)
- glossary/ లొకేల్ వారీగా గ్లాసరీ మేనేజ్మెంట్
- llm/ LLM ప్రొవైడర్ ఇంటర్ఫేస్ + ఇంప్లిమెంటేషన్స్
- anthropic.go Anthropic Claude బ్యాకెండ్
- openai.go OpenAI / అనుకూల బ్యాకెండ్
- gemini.go AI Studio బ్యాకెండ్ ద్వారా Google Gemini
- OpenRouter కస్టమ్ base_url తో openai.go ని ఉపయోగిస్తుంది
- styleguide/ స్టైల్ గైడ్ లోడర్
- tm/ JSONL ట్రాన్స్లేషన్ మెమరీ
- translate/ ట్రాన్స్లేషన్ ఆర్కెస్ట్రేటర్
- validate/ లొకేల్ వాలిడేషన్ మరియు డిఫింగ్
+ config/ YAML config loading with defaults
+ detect/ Project type auto-detection
+ formats/ Format parsers (JSON, YAML, Markdown)
+ glossary/ Per-locale glossary management
+ llm/ LLM provider interface + implementations
+ anthropic.go Anthropic Claude backend
+ openai.go OpenAI / compatible backend
+ gemini.go Google Gemini via AI Studio backend
+ OpenRouter uses openai.go with custom base_url
+ locale/ BCP 47 identity and CLDR plural categories
+ message/ ICU MessageFormat parser and structural comparison
+ policy/ Stable translation-policy hashing
+ state/ Versioned translation manifest
+ styleguide/ Style guide loader
+ tm/ JSONL translation memory
+ translate/ Translation orchestrator
+ validate/ Locale validation and diffing
```
+
## ప్రత్యామ్నాయాలతో పోలిక
| ఫీచర్ | Internationalizer | i18next | Crowdin | సాధారణ LLM |
|---------|------------------|---------|---------|-------------|
| LLM-ఆధారిత అనువాదం | అవును | కాదు | పాక్షికం | అవును |
| భాషల వారీగా స్టైల్ గైడ్లు | అవును | కాదు | కాదు | కాదు |
-| గ్లాసరీ ఎన్ఫోర్స్మెంట్ | అవును | కాదు | అవును | కాదు |
+| గ్లాసరీ అమలు | అవును | కాదు | అవును | కాదు |
| ట్రాన్స్లేషన్ మెమరీ | అవును | కాదు | అవును | కాదు |
| CLI / లోకల్ ఎగ్జిక్యూషన్ | అవును | వర్తించదు | కాదు | మాన్యువల్ |
| Git-ఫ్రెండ్లీ ఫైల్లు | అవును | అవును | పాక్షికం | మాన్యువల్ |
| SaaS డిపెండెన్సీ లేదు | అవును | అవును | కాదు | మారుతుంది |
| ఓపెన్ సోర్స్ (AGPL-3.0) | అవును | అవును | కాదు | మారుతుంది |
+
## లైసెన్స్
-[AGPL-3.0](LICENSE)
+[AGPL-3.0](../../LICENSE)
-## కాంట్రిబ్యూటింగ్
+డిపెండెన్సీ నోటీసుల కోసం [THIRD_PARTY_NOTICES.md](../../THIRD_PARTY_NOTICES.md) చూడండి.
-డెవలప్మెంట్ సెటప్ మరియు మార్గదర్శకాల కోసం [CONTRIBUTING.md](CONTRIBUTING.md) చూడండి. అన్ని కాంట్రిబ్యూషన్లకు DCO సైన్-ఆఫ్ అవసరం.
+
+## సహకారం
+డెవలప్మెంట్ సెటప్ మరియు మార్గదర్శకాల కోసం [CONTRIBUTING.md](../../CONTRIBUTING.md) చూడండి. అన్ని కాంట్రిబ్యూషన్లకు DCO సైన్-ఆఫ్ అవసరం.
diff --git a/docs/i18n/th.md b/docs/i18n/th.md
index c890fd1..d7a9703 100644
--- a/docs/i18n/th.md
+++ b/docs/i18n/th.md
@@ -6,26 +6,32 @@
# Internationalizer
-ไปป์ไลน์ internationalization แบบ AI-native สำหรับโปรเจกต์ซอฟต์แวร์ แปล ตรวจสอบ และจัดการไฟล์ i18n โดยใช้ LLM
+ไปป์ไลน์ internationalization แบบ AI-native สำหรับโปรเจกต์ซอฟต์แวร์ แปล ตรวจสอบความถูกต้อง และจัดการไฟล์ i18n โดยใช้ LLM
[](https://github.com/Tom-R-Main/Internationalizer/actions/workflows/ci.yml)
-[](LICENSE)
+[](../../LICENSE)
-## ทำไมต้อง Internationalizer?
+
+العربية · বাংলা · Čeština · Dansk · Deutsch · Ελληνικά · Español · Suomi · Français · עברית · हिन्दी · Indonesia · Italiano · 日本語 · 한국어 · Bahasa Melayu
Nederlands · ਪੰਜਾਬੀ · Polski · Português · Română · Русский · Svenska · తెలుగు · ไทย · Türkçe · Українська · Tiếng Việt · 粵語 · 简体中文 · 繁體中文
+
-เครื่องมือ i18n ส่วนใหญ่มักจะเป็น runtime libraries (i18next, react-intl) หรือแพลตฟอร์ม SaaS สำหรับจัดการคีย์ (Crowdin, Lokalise) ซึ่งไม่มีเครื่องมือใดที่แก้ปัญหาการแปลได้อย่างแท้จริง:
+---
+
+## ทำไมต้อง Internationalizer?
-- **การแปลด้วยตนเอง (Manual translation)** ไม่สามารถขยายสเกลได้เมื่อมีหลายภาษา
-- **Machine translation APIs** (Google Translate, DeepL) ละเลยคำศัพท์ โทนเสียง และรูปแบบ UI ของคุณ
-- **การแปลด้วย LLM ทั่วไป** ทำงานได้ดีกว่า แต่หากไม่มีอภิธานศัพท์ (glossaries) และคู่มือสไตล์ (style guides) ผลลัพธ์ที่ได้ก็จะไม่สม่ำเสมอ
+เครื่องมือ i18n ส่วนใหญ่เป็นไลบรารีระดับรันไทม์ (i18next, react-intl) หรือไม่ก็แพลตฟอร์ม SaaS สำหรับจัดการคีย์ (Crowdin, Lokalise) ซึ่งไม่มีเครื่องมือใดที่แก้ปัญหาด้านการแปลได้อย่างแท้จริง:
-Internationalizer นั้นแตกต่างออกไป มันคือ **CLI pipeline** ที่ผสานรวมการแปลด้วย LLM เข้ากับ:
+- **การแปลด้วยตนเอง** ไม่สามารถขยายขนาดได้เมื่อมีมากกว่าสองสามภาษา
+- **API การแปลภาษาด้วยเครื่อง** (Google Translate, DeepL) ละเลยคำศัพท์เฉพาะ โทนเสียง และแบบแผน UI ของคุณ
+- **การแปลด้วย LLM ทั่วไป** ให้ผลลัพธ์ที่ดีกว่า แต่หากปราศจากอภิธานศัพท์และคู่มือสไตล์ ผลลัพธ์ที่ได้จะขาดความสม่ำเสมอ
-- **อภิธานศัพท์แยกตามภาษา** — บังคับใช้คำศัพท์ให้สอดคล้องกันทั่วทั้งแอปของคุณ
-- **คู่มือสไตล์แยกตามภาษา** — ควบคุมโทนเสียง ระดับความเป็นทางการ การทำพหูพจน์ และการจัดรูปแบบตัวอักษร
-- **หน่วยความจำการแปล (Translation memory)** — ข้ามข้อความที่ไม่มีการเปลี่ยนแปลง ช่วยประหยัดค่าใช้จ่ายในการเรียก API
-- **การตรวจสอบคีย์ (Key validation)** — ตรวจจับการแปลที่ตกหล่นและตัวแปร interpolation ที่ไม่ตรงกันก่อนนำไปใช้งานจริง
+Internationalizer แตกต่างออกไป เพราะนี่คือ **CLI pipeline** ที่ผสานการแปลด้วย LLM เข้ากับ:
+- **อภิธานศัพท์เฉพาะแต่ละภาษา** — บังคับใช้คำศัพท์ให้สอดคล้องกันทั่วทั้งแอปของคุณ
+- **คู่มือสไตล์เฉพาะแต่ละภาษา** — ควบคุมโทนเสียง ระดับความเป็นทางการ การผันพหูพจน์ และการจัดรูปแบบตัวพิมพ์
+- **หน่วยความจำการแปล** — ข้ามสตริงที่ไม่มีการเปลี่ยนแปลง ช่วยประหยัดค่าใช้จ่ายในการเรียกใช้ API
+- **การตรวจสอบความถูกต้องแบบกำหนดแน่นอน** — ดักจับคีย์ที่หายไปหรือเกินมา โครงสร้างที่มีการป้องกันคลาดเคลื่อน ปัญหาอภิธานศัพท์ ตลอดจนข้อผิดพลาดของพหูพจน์หรือ ICU ก่อนที่จะนำไปใช้งานจริง
+
## การติดตั้ง
ติดตั้งจาก npm:
@@ -34,13 +40,13 @@ Internationalizer นั้นแตกต่างออกไป มันค
npm install -g internationalizer
```
-หรือรันโดยไม่ต้องติดตั้งแบบ global:
+หรือรันโดยไม่ต้องติดตั้งแบบส่วนกลาง:
```bash
npx internationalizer --help
```
-แพ็กเกจ npm จะติดตั้ง prebuilt binary ที่ตรงกันจาก npm ผ่าน optional dependencies ที่เฉพาะเจาะจงกับแต่ละแพลตฟอร์ม
+แพ็กเกจ npm จะติดตั้งไบนารีที่สร้างไว้ล่วงหน้าซึ่งตรงกับระบบจาก npm ผ่าน optional dependencies เฉพาะของแต่ละแพลตฟอร์ม
ติดตั้งด้วย Go:
@@ -55,23 +61,27 @@ git clone https://github.com/Tom-R-Main/Internationalizer.git
cd Internationalizer
go build -o internationalizer ./cmd/internationalizer
```
-
+
## แพ็กเกจ npm
-- Git tags และเวอร์ชันของแพ็กเกจ npm ต้องตรงกัน ตัวอย่างเช่น `v0.1.0` และ `0.1.0`
-- แพ็กเกจ `internationalizer` หลักจะขึ้นอยู่กับแพ็กเกจของแพลตฟอร์ม เช่น `internationalizer-darwin-arm64`
-- เป้าหมาย npm ที่รองรับ: macOS arm64/x64, Linux arm64/x64, Windows x64
-- การเผยแพร่ผ่าน CI จำเป็นต้องใช้ GitHub secret ที่ชื่อว่า `NPM_TOKEN`
-
-## เริ่มต้นใช้งานอย่างรวดเร็ว
+- แท็ก Git และเวอร์ชันของแพ็กเกจ npm ต้องตรงกัน เช่น `v0.1.0` และ `0.1.0`
+- แพ็กเกจราก `internationalizer` ขึ้นอยู่กับแพ็กเกจประจำแต่ละแพลตฟอร์ม เช่น `internationalizer-darwin-arm64`
+- แพลตฟอร์มเป้าหมาย npm ที่รองรับ: macOS arm64/x64, Linux arm64/x64, Windows x64
+- การเผยแพร่ผ่าน CI ต้องใช้ GitHub secret ที่ชื่อว่า `NPM_TOKEN`
+
+## เริ่มต้นใช้งานด่วน
-1. สร้างไฟล์คอนฟิกใน root ของโปรเจกต์คุณ:
+1. สร้างไฟล์คอนฟิกที่ไดเรกทอรีรากของโปรเจกต์:
```yaml
# .internationalizer.yml
source_locale: en
target_locales: [fr, de, es, ja]
-source_path: locales/en.json
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
llm:
provider: gemini
@@ -79,57 +89,85 @@ llm:
api_key_env: GOOGLE_AI_STUDIO_API_KEY
```
-2. ตั้งค่า API key ของคุณ:
+2. กำหนดค่า API key ของคุณ:
```bash
export GOOGLE_AI_STUDIO_API_KEY=your-ai-studio-key
```
-3. ดูตัวอย่างสิ่งที่จะถูกแปล:
+3. ดูตัวอย่างรายการที่จะถูกแปล:
```bash
internationalizer translate --dry-run
```
-4. รันการแปล:
+4. ดำเนินการแปล:
```bash
internationalizer translate
```
-5. ตรวจสอบ locales ทั้งหมด:
+5. ตรวจสอบความถูกต้องของทุกภาษา:
```bash
internationalizer validate
```
-
+
## คำสั่ง
### `translate`
-ค้นหาคีย์ที่หายไปและแปลผ่าน LLM
+ค้นหาคีย์ที่หายไปหรือล้าสมัย แล้วแปลผ่าน LLM
```bash
-internationalizer translate # แปล locales ทั้งหมด
-internationalizer translate -l fr # แปลเฉพาะภาษาฝรั่งเศส
-internationalizer translate --dry-run # ดูตัวอย่างโดยไม่เรียก API
-internationalizer translate --batch-size 20 # ลดขนาด batch
-internationalizer translate --concurrency 2 # ลดการเรียกพร้อมกัน (parallel)
+internationalizer translate # translate all locales
+internationalizer translate -l fr # translate French only
+internationalizer translate --dry-run # preview without API calls
+internationalizer translate --adopt-existing # baseline existing translations without API calls
+internationalizer translate --refresh-policy # refresh prompt/style/model-stale entries
+internationalizer translate --batch-size 20 # smaller batches
+internationalizer translate --concurrency 2 # fewer parallel calls
```
+สถานะการแปลจะรายงานเงื่อนไข missing, source-stale, policy-stale, current และการแก้ไขด้วยตนเองอย่างเป็นอิสระต่อกัน ดังนั้นการแก้ไขด้วยตนเองจึงไม่สามารถบดบังการเปลี่ยนแปลงของต้นทางหรือนโยบายได้ ค่าที่เข้าข่าย policy-stale จะได้รับการรายงานแต่จะถูกแปลใหม่เมื่อระบุแฟล็ก `--refresh-policy` เท่านั้น ส่วนค่าที่มีการแก้ไขด้วยตนเองจะไม่ถูกเขียนทับโดยอัตโนมัติ ให้ใช้ `--adopt-existing` เมื่อนำ manifest เข้าสู่ชุดคำแปลที่ผ่านการตรวจสอบแล้ว หรือเมื่อต้องการยอมรับการแก้ไขด้วยตนเองที่ตรวจสอบแล้วให้เป็นเกณฑ์มาตรฐานใหม่อย่างชัดเจน
+
### `validate`
-ตรวจสอบไฟล์ locale ทั้งหมดเพื่อหาคีย์ที่หายไป คีย์ที่เกินมา และตัวแปร interpolation ที่ไม่ตรงกัน
+ตรวจสอบไฟล์ภาษาทั้งหมดเทียบกับชุดบันเดิลต้นทาง การตรวจสอบความถูกต้องตามค่าเริ่มต้นจะตรวจเช็กความครอบคลุมเชิงโครงสร้าง (สัดส่วนร้อยละของคีย์ปลายทางที่จำเป็นทั้งหมดที่มีอยู่) รายงานคีย์ส่วนเกินเป็นคำเตือน และจะล้มเหลวทันทีหากพบคีย์ที่หายไป ตัวแปร interpolation ไม่ตรงกัน หรือโครงสร้าง ICU MessageFormat ไม่ถูกต้อง
```bash
-internationalizer validate # แสดงผลลัพธ์ที่อ่านง่ายสำหรับมนุษย์
-internationalizer validate --json # แสดงผลลัพธ์เป็น JSON สำหรับเครื่อง
-internationalizer validate -q # แสดงเฉพาะ exit code
+internationalizer validate # human-readable output
+internationalizer validate --json # machine-readable JSON
+internationalizer validate -q # exit code only
+internationalizer validate --strict # enforce translation quality rules
+internationalizer validate --require-state # require current manifest provenance
```
+`--strict` จะรายงานความครอบคลุมของการแปลด้วย ค่าเชิงภาษาที่เหมือนกับต้นทางจะถือว่ายังไม่ได้แปล เว้นแต่อภิธานศัพท์จะมีการระบุรายการสำหรับค่านั้นอย่างชัดเจนที่ตรงกันทั้งค่าต้นทางและปลายทางที่เหมือนกันอย่างสมบูรณ์ ทั้งนี้จะยังคงเคารพการตั้งค่า `ignore_case` แต่คำศัพท์ในอภิธานศัพท์ที่ฝังอยู่ภายในค่าที่ยาวกว่าจะไม่ได้รับการยกเว้น โหมดเข้มงวดจะล้มเหลวเมื่อพบคีย์ส่วนเกิน ค่าที่เหมือนกับต้นทาง โครงสร้าง interpolation/HTML/โค้ด/ลิงก์ Markdown ที่เปลี่ยนแปลง การละเมิดอภิธานศัพท์ ตลอดจนรูปพหูพจน์ที่มีการกำหนดค่าไว้
+
+`--require-state` จะตรวจสอบความถูกต้องของแต่ละปลายทางเทียบกับ `.internationalizer.lock` โดยจะล้มเหลวเมื่อคีย์ไม่ได้ถูกติดตาม หรือเมื่อแฮชของต้นทาง นโยบายการแปล หรือปลายทางที่บันทึกไว้ล้าสมัย แฟล็กนี้สามารถใช้ร่วมกับ `--strict` ได้
+
+รายงานทั้งแบบสำหรับมนุษย์และแบบ JSON จะใช้รหัสผลการตรวจพบที่คงที่:
+
+| รหัส | ความหมาย |
+| --- | --- |
+| `missing_key` / `extra_key` | ชุดคีย์ต้นทางและปลายทางแตกต่างกัน |
+| `blank_translation` | ต้นทางมีเนื้อหา แต่ปลายทางในโหมดเข้มงวดว่างเปล่า |
+| `source_identical` | ค่าเชิงภาษาในโหมดเข้มงวดยังคงไม่ได้รับการแปล |
+| `protected_structure_mismatch` | โครงสร้าง interpolation, HTML, โค้ด หรือลิงก์เกิดการเปลี่ยนแปลง |
+| `glossary_violation` | ไม่พบคำหรือรูปแบบย่อยปลายทางที่ได้รับอนุมัติ |
+| `plural_form_missing` | ขาดรูปพหูพจน์ตามที่กำหนดค่าไว้สำหรับภาษานั้น |
+| `icu_message_syntax` | โครงสร้างข้อความ ICU ของต้นทางหรือปลายทางมีรูปแบบไม่ถูกต้อง |
+| `icu_argument_mismatch` | ชื่อ ชนิด หรือสไตล์ตัวจัดรูปแบบของอาร์กิวเมนต์ ICU แตกต่างกัน |
+| `icu_selector_mismatch` | ตัวเลือกแตกต่างกัน หรือหมวดหมู่พหูพจน์ไม่ถูกต้องสำหรับภาษาปลายทาง |
+| `untracked` | ไม่มีบันทึก manifest สำหรับปลายทางนี้ |
+| `source_stale` | เนื้อหาต้นทางเปลี่ยนแปลงไปหลังจากการบันทึกการแปล |
+| `policy_stale` | พรอมต์ที่สร้างขึ้นหรือการตั้งค่าโมเดลมีการเปลี่ยนแปลง |
+| `target_modified` | เนื้อหาปลายทางแตกต่างจากบันทึกใน manifest |
+
### `detect`
-ตรวจจับเฟรมเวิร์ก i18n อัตโนมัติและแนะนำการตั้งค่า
+ตรวจหาเฟรมเวิร์ก i18n อัตโนมัติและแนะนำการกำหนดค่าที่เหมาะสม
```bash
internationalizer detect
@@ -139,7 +177,7 @@ internationalizer detect
### `glossary`
-จัดการคำศัพท์ในอภิธานศัพท์แยกตามภาษา ซึ่งจะถูกบังคับใช้ในระหว่างการแปล
+จัดการคำศัพท์ในอภิธานศัพท์เฉพาะแต่ละภาษาที่ถูกบังคับใช้ระหว่างการแปล
```bash
internationalizer glossary list --locale fr
@@ -149,166 +187,225 @@ internationalizer glossary remove --locale fr --source "Dashboard"
### `tm`
-จัดการหน่วยความจำการแปล (แคช JSONL ของข้อความที่เคยแปลไปแล้ว)
+จัดการหน่วยความจำการแปล (แคช JSONL สำหรับสตริงที่เคยแปลแล้ว)
```bash
-internationalizer tm stats # แสดงจำนวนเรกคอร์ด
-internationalizer tm export # ดัมป์เป็น JSON
-internationalizer tm clear --force # ลบเรกคอร์ดทั้งหมด
+internationalizer tm stats # show record counts
+internationalizer tm export # dump as JSON
+internationalizer tm clear --force # delete all records
```
-
-## ข้อมูลอ้างอิงการตั้งค่า (Configuration Reference)
+
+## ข้อมูลอ้างอิงการกำหนดค่า
```yaml
# .internationalizer.yml
-# ภาษาต้นทาง (ค่าเริ่มต้น: en)
+# Source language (default: en)
source_locale: en
-# ภาษาที่ต้องการแปล (จำเป็น)
-target_locales: [fr, de, es, ja, zh-CN, ar]
-
-# พาธไปยังไฟล์ locale ต้นทาง (จำเป็น)
-source_path: locales/en.json
-
-# การตั้งค่าผู้ให้บริการ LLM
+# Languages to translate into (required)
+target_locales: [fr, de, es, ja, yue, zh-CN, zh-TW, ar]
+
+# One or more source-to-target mappings (required).
+# {locale} is replaced with each configured target locale.
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
+ - id: docs
+ source: README.md
+ target: docs/i18n/{locale}.md
+ format: markdown
+
+# Backward compatibility: source_path still maps targets to sibling files
+# such as locales/fr.json. Prefer bundles for new projects.
+# source_path: locales/en.json
+
+# LLM provider settings
llm:
- # ผู้ให้บริการ: "anthropic", "openai", "gemini" หรือ "openrouter" (ค่าเริ่มต้น: gemini)
+ # Provider: "anthropic", "openai", "gemini", or "openrouter" (default: gemini)
provider: gemini
- # ชื่อโมเดลเริ่มต้นตามผู้ให้บริการ:
+ # Model name defaults by provider:
# anthropic: claude-opus-5
- # openai: gpt-5.6-luna
+ # openai: gpt-5.6-luna (reasoning effort defaults to max)
# gemini: gemini-3.8-flash
# openrouter: deepseek/deepseek-v4-pro-0813
model: gemini-3.8-flash
- # ตัวแปรสภาพแวดล้อม (Environment variable) ที่เก็บ API key
+ # Environment variable containing the API key
api_key_env: GOOGLE_AI_STUDIO_API_KEY
- # Base URL สำหรับ endpoint ที่เข้ากันได้กับ OpenAI (ไม่บังคับ)
+ # Base URL for OpenAI-compatible endpoints (optional)
# base_url: https://api.openai.com
-# จำนวนคีย์ต่อการเรียก LLM หนึ่งครั้ง (ค่าเริ่มต้น: 40)
+ # OpenAI GPT-5-series Responses API reasoning effort
+ # (default: max for the OpenAI provider)
+ reasoning_effort: max
+
+ # Optional LLM settings for individual target locales. An override using the
+ # global provider inherits unspecified global settings. A different provider
+ # uses that provider's defaults for unspecified settings.
+ locale_overrides:
+ yue:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-CN:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-TW:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+
+# Keys per LLM call (default: 40)
batch_size: 40
-# การเรียก LLM พร้อมกัน (ค่าเริ่มต้น: 4)
+# Parallel LLM calls (default: 4)
concurrency: 4
-# ไดเรกทอรีที่เก็บไฟล์ Markdown คู่มือสไตล์แยกตามภาษา (ค่าเริ่มต้น: style-guides)
+# Directory containing per-locale style guide Markdown files (default: style-guides)
style_guides_dir: style-guides
-# ไดเรกทอรีที่เก็บไฟล์ JSON อภิธานศัพท์แยกตามภาษา (ค่าเริ่มต้น: glossary)
+# Directory containing per-locale glossary JSON files (default: glossary)
glossary_dir: glossary
-# พาธไปยังไฟล์หน่วยความจำการแปล (ค่าเริ่มต้น: .internationalizer/tm.jsonl)
+# Path to translation memory file (default: .internationalizer/tm.jsonl)
tm_path: .internationalizer/tm.jsonl
+
+# Versioned source, policy, target, and provenance state
+# (default: .internationalizer.lock; commit this file)
+manifest_path: .internationalizer.lock
+
+# Optional translation and strict-validation rules
+validation:
+ plural_style: i18next-v4 # generate and validate target-locale plural forms
```
-## คู่มือสไตล์ (Style Guides)
+ตัวระบุภาษาต้องเป็นแท็ก BCP 47 ที่มีรูปแบบถูกต้อง เช่น `fr`, `pt-BR` หรือ `sr-Latn-RS` ค่าภาษาเป้าหมายที่เทียบเท่าเชิงมาตรฐาน (canonical-equivalent) จะถูกปฏิเสธเป็นรายการซ้ำซ้อน และการเขียนทับผู้ให้บริการเฉพาะภาษาจะจับคู่ตามการสะกดที่เทียบเท่าเชิงมาตรฐาน ในตัวอย่างข้างต้น ภาษาที่ไม่มีการกำหนดค่าเขียนทับ—ซึ่งรวมถึงภาษาญี่ปุ่น—จะสืบทอดการกำหนดค่าสากลของ Gemini
+
+ค่าของ ICU MessageFormat จะได้รับการแจงส่วนเชิงโครงสร้าง รองรับอาร์กิวเมนต์แบบพื้นฐาน, `select`, `plural`, `selectordinal`, `number`, `date` และ `time` ตลอดจนข้อความซ้อนในตัว, ออฟเซ็ตของพหูพจน์, ตัวเลือกตัวเลขที่แน่นอน และ `#` การตรวจสอบความถูกต้องจะตรวจสอบไวยากรณ์ ชนิดอาร์กิวเมนต์ สไตล์ตัวจัดรูปแบบ ออฟเซ็ตพหูพจน์ ความตรงกันของสาขา select และหมวดหมู่พหูพจน์ CLDR ของภาษาเป้าหมาย หากผลลัพธ์จากผู้ให้บริการละเมิดข้อกำหนดเหล่านี้ ระบบจะปฏิเสธก่อนที่จะเขียนไฟล์ภาษาหรือเรกคอร์ดลงหน่วยความจำการแปล
+
+สำหรับ `i18next-v4` กลุ่มพหูพจน์ต้นทางที่ระบบรู้จักจะถูกขยายระหว่างการแปลให้สอดคล้องกับหมวดหมู่ CLDR ของภาษาเป้าหมาย หมวดหมู่ที่มีเฉพาะในภาษาเป้าหมายจะใช้ค่า `_other` ของกลุ่มต้นทางเป็นเทมเพลตสำหรับการแปล ทั้งนี้การตรวจสอบความถูกต้องแบบเข้มงวดจะกำหนดให้ต้องมีหมวดหมู่ปลายทางเหล่านั้นครบถ้วน ส่วนหมวดหมู่ที่มีเฉพาะในภาษาต้นทางจะถือเป็นทางเลือกสำหรับภาษาเป้าหมายที่ไม่ได้ใช้งาน
+
+## คู่มือสไตล์
-คู่มือสไตล์คือไฟล์ Markdown ที่จะถูกแทรกเข้าไปใน prompt การแปลของ LLM ซึ่งจะช่วยควบคุมโทนเสียง ระดับความเป็นทางการ การจัดรูปแบบตัวอักษร และธรรมเนียมปฏิบัติเฉพาะของแต่ละภาษา
+คู่มือสไตล์คือไฟล์ Markdown ที่จะถูกแทรกลงในพรอมต์การแปลของ LLM เพื่อควบคุมโทนเสียง ระดับความเป็นทางการ การจัดรูปแบบตัวพิมพ์ และแบบแผนเฉพาะภาษาอื่นๆ
```
style-guides/
- _conventions.md # กฎที่ใช้ร่วมกันสำหรับทุกภาษา
- fr.md # กฎเฉพาะสำหรับภาษาฝรั่งเศส
- ja.md # กฎเฉพาะสำหรับภาษาญี่ปุ่น
- ar.md # กฎเฉพาะสำหรับภาษาอาหรับ
+ _conventions.md # shared rules for all languages
+ fr.md # French-specific rules
+ ja.md # Japanese-specific rules
+ ar.md # Arabic-specific rules
```
-### ธรรมเนียมปฏิบัติที่ใช้ร่วมกัน (`_conventions.md`)
+### ข้อตกลงร่วมกัน (`_conventions.md`)
-กำหนดกฎที่ใช้กับทุกภาษา: ไวยากรณ์ interpolation, การคงแท็ก HTML ไว้, ธรรมเนียมปฏิบัติของประเภทข้อความ (ปุ่ม vs. ป้ายกำกับ vs. ข้อผิดพลาด) เป็นต้น
+กำหนดกฎที่ใช้ร่วมกันกับทุกภาษา: ไวยากรณ์ interpolation, การคงโครงสร้าง HTML, แบบแผนประเภทของสตริง (ปุ่ม เทียบกับ ป้ายกำกับ เทียบกับ ข้อผิดพลาด) เป็นต้น
-### คู่มือแยกตามภาษา (`{locale}.md`)
+### คู่มือเฉพาะแต่ละภาษา (`{locale}.md`)
-กำหนดกฎเฉพาะของแต่ละภาษา: ระดับความเป็นทางการ (tu vs. vous), เครื่องหมายวรรคตอน (guillemets, เครื่องหมายคำถามกลับหัว), รูปพหูพจน์, การจัดรูปแบบวันที่/ตัวเลข และอภิธานศัพท์
+กำหนดกฎเฉพาะของแต่ละภาษา: ระดับความเป็นทางการ (tu เทียบกับ vous), เครื่องหมายวรรคตอน (guillemets, เครื่องหมายคำถามกลับหัว), รูปพหูพจน์, การจัดรูปแบบวันที่/ตัวเลข และอภิธานศัพท์คำศัพท์
-ดูตัวอย่างการใช้งานจริงได้ที่ [`examples/react-app/style-guides/`](examples/react-app/style-guides/)
+คู่มือสไตล์เป็นอินพุตเชิงนโยบายที่คงทน ไม่ใช่ผลลัพธ์ที่สร้างขึ้น Internationalizer จะอ่านคู่มือเหล่านี้แต่จะไม่เขียนทับอย่างเด็ดขาด เนื้อหาจะถูกสร้างแฮชแยกต่างหากจากอภิธานศัพท์และข้อกำหนดของพรอมต์ ดังนั้นการเปลี่ยนแปลงโค้ดของแอปพลิเคชันจะไม่ทำให้การแปลกลายเป็นสถานะล้าสมัย การแก้ไขคู่มือถือเป็นความตั้งใจทำเครื่องหมายภาษานั้นเพื่อตรวจสอบนโยบาย แต่การเปลี่ยนถ้อยคำภายในพรอมต์จะไม่ส่งผล เว้นแต่เวอร์ชันข้อกำหนดของพรอมต์จะมีการเปลี่ยนแปลงด้วยเช่นกัน
-## รูปแบบอภิธานศัพท์ (Glossary Format)
+ดูตัวอย่างการใช้งานจริงได้ที่ [`examples/react-app/style-guides/`](../../examples/react-app/style-guides/)
+
+## รูปแบบอภิธานศัพท์
-ไฟล์อภิธานศัพท์เป็น JSON arrays ที่จัดเก็บไว้ใน `{glossary_dir}/{locale}.json`:
+ไฟล์อภิธานศัพท์เป็นอาร์เรย์ JSON ที่จัดเก็บไว้ใน `{glossary_dir}/{locale}.json`:
```json
[
{
"source": "Dashboard",
"target": "Tableau de bord",
+ "variants": ["Panneau de contrôle"],
+ "enforcement": "error",
"ignore_case": false,
"whole_word": true
}
]
```
-คำศัพท์ต่างๆ จะถูกแทรกเข้าไปใน prompt ของ LLM ในรูปแบบตารางคำศัพท์ เพื่อให้แน่ใจว่าคำศัพท์สำคัญต่างๆ จะถูกแปลอย่างสอดคล้องกันทั่วทั้งแอปพลิเคชันของคุณ
+`variants` แสดงรายการรูปแบบปลายทางอื่นๆ ที่ได้รับอนุมัติ ส่วน `enforcement` อาจระบุเป็น `error`, `warning` หรือละเว้นไว้เพื่อใช้พฤติกรรมข้อผิดพลาดตามค่าเริ่มต้น คำศัพท์จะถูกส่งเข้าไปในพรอมต์ของ LLM ในรูปแบบตารางคำศัพท์ เพื่อให้มั่นใจได้ว่าการแปลทั่วทั้งแอปพลิเคชันจะสอดคล้องกัน รายการที่ระบุค่าตรงกันทุกประการ เช่น `{"source":"API","target":"API"}` จะช่วยยกเว้นค่านั้นทั้งค่าที่เหมือนกับต้นทางจากการตรวจพบว่าเป็นค่าที่ยังไม่ได้แปลในโหมดเข้มงวดด้วย แต่จะไม่ยกเว้นข้อความที่ยาวกว่าซึ่งเพียงแค่มีคำว่า `API` อยู่ภายใน
+
+## หน่วยความจำการแปล
-## หน่วยความจำการแปล (Translation Memory)
+หน่วยความจำการแปลจะจัดเก็บในรูปแบบไฟล์ JSONL (หนึ่งเรกคอร์ด JSON ต่อหนึ่งบรรทัด) แต่ละเรกคอร์ดประกอบด้วย:
-หน่วยความจำการแปลจะถูกจัดเก็บเป็นไฟล์ JSONL (หนึ่ง JSON record ต่อบรรทัด) แต่ละเรกคอร์ดประกอบด้วย:
-
-- คีย์และค่าของต้นทาง
-- ค่าที่แปลแล้ว
-- SHA-256 hash ของค่าต้นทาง
-- Timestamp
-
-ในการรันครั้งถัดไป ข้อความที่ไม่มีการเปลี่ยนแปลงจะถูกดึงมาจากแคช TM โดยไม่ต้องเรียก LLM ซึ่งช่วยประหยัดทั้งเวลาและค่าใช้จ่าย API ไฟล์ TM นี้เป็นมิตรกับ git และสามารถ commit ไปพร้อมกับไฟล์ locale ของคุณได้
+- บันเดิล คีย์ ค่าต้นทาง ค่าที่แปลแล้ว และภาษาปลายทางตามรูปแบบมาตรฐาน
+- แฮชของต้นทาง คู่มือสไตล์ อภิธานศัพท์ ข้อกำหนดของพรอมต์ และนโยบายโดยรวม
+- ผู้ให้บริการและโมเดลที่ทำการแปล
+- การประทับเวลา
+ในการรันครั้งต่อๆ ไป ข้อความที่มีแฮชของต้นทางและนโยบายตรงกันจะถูกดึงมาจากแคชทันทีโดยไม่ต้องเรียกใช้ LLM พาธเริ่มต้นจะอยู่ภายใต้ไดเรกทอรี `.internationalizer/` ซึ่งถูกละเว้นไว้ จึงทำงานเป็นแคชเฉพาะเครื่อง หากโปรเจกต์ของคุณต้องการแชร์หน่วยความจำการแปลร่วมกัน ให้กำหนด `tm_path` ไปยังตำแหน่งที่มีการติดตามในระบบควบคุมเวอร์ชัน ส่วน manifest `.internationalizer.lock` ที่สามารถตรวจสอบได้จะมีการกำหนดเวอร์ชันแยกต่างหาก
+
## รูปแบบที่รองรับ
| รูปแบบ | นามสกุลไฟล์ | โหมด |
|--------|-----------|------|
-| JSON | `.json` | Key-value (ซ้อนทับกันได้, แบนราบด้วย dot-notation) |
+| JSON | `.json` | Key-value (ซ้อนกันได้, แบนราบด้วย dot-notation) |
| YAML | `.yml`, `.yaml` | Key-value (คงคอมเมนต์และลำดับไว้) |
-| Markdown | `.md`, `.mdx` | แปลทั้งเอกสาร |
+| Markdown | `.md`, `.mdx` | ส่วนนำและส่วนย่อยระดับ H2 |
-## การตรวจจับประเภทโปรเจกต์
+ไฟล์ปลายทาง Markdown จะมีคอมเมนต์ `internationalizer:unit` ที่มองไม่เห็นกำกับไว้ก่อนส่วนย่อยระดับ H2 เครื่องหมายที่เสถียรเหล่านี้ช่วยให้ Internationalizer สามารถเพิ่ม ย้าย หรือแก้ไขเนื้อหาต้นทางส่วนใดส่วนหนึ่งได้โดยไม่ต้องแปลส่วนย่อยอื่นที่ไม่เกี่ยวข้องซ้ำ เอกสารเดิมที่ยังไม่มีเครื่องหมายกำกับจะได้รับเครื่องหมายนี้ในการอัปเดตที่สำเร็จครั้งถัดไป
+
+## การตรวจหาประเภทโปรเจกต์
-`internationalizer detect` จะระบุการตั้งค่า i18n ของคุณโดยตรวจสอบ:
+คำสั่ง `internationalizer detect` จะระบุการตั้งค่า i18n ของคุณโดยตรวจสอบจาก:
- dependencies ใน `package.json` สำหรับ react-i18next, next-intl หรือ vue-i18n
-- โครงสร้างไดเรกทอรีที่ตรงกับรูปแบบ locale ทั่วไป
-- นามสกุลไฟล์และธรรมเนียมการตั้งชื่อ
-
-## สถาปัตยกรรม (Architecture)
+- โครงสร้างไดเรกทอรีที่ตรงกับแบบแผนทั่วไปของภาษา
+- นามสกุลไฟล์และแบบแผนการตั้งชื่อ
+
+## สถาปัตยกรรม
```
-cmd/internationalizer/ จุดเริ่มต้นของ CLI และการกำหนดคำสั่ง
+cmd/internationalizer/ CLI entry point and command definitions
internal/
- config/ โหลดคอนฟิก YAML พร้อมค่าเริ่มต้น
- detect/ ตรวจจับประเภทโปรเจกต์อัตโนมัติ
- formats/ ตัวแยกวิเคราะห์รูปแบบ (JSON, YAML, Markdown)
- glossary/ การจัดการอภิธานศัพท์แยกตามภาษา
- llm/ อินเทอร์เฟซผู้ให้บริการ LLM + การนำไปใช้งาน
- anthropic.go แบ็กเอนด์ Anthropic Claude
- openai.go แบ็กเอนด์ OpenAI / ที่เข้ากันได้
- gemini.go แบ็กเอนด์ Google Gemini ผ่าน AI Studio
- OpenRouter ใช้ openai.go พร้อม base_url แบบกำหนดเอง
- styleguide/ ตัวโหลดคู่มือสไตล์
- tm/ หน่วยความจำการแปล JSONL
- translate/ ตัวจัดการการแปล
- validate/ การตรวจสอบ locale และการหาความแตกต่าง
+ config/ YAML config loading with defaults
+ detect/ Project type auto-detection
+ formats/ Format parsers (JSON, YAML, Markdown)
+ glossary/ Per-locale glossary management
+ llm/ LLM provider interface + implementations
+ anthropic.go Anthropic Claude backend
+ openai.go OpenAI / compatible backend
+ gemini.go Google Gemini via AI Studio backend
+ OpenRouter uses openai.go with custom base_url
+ locale/ BCP 47 identity and CLDR plural categories
+ message/ ICU MessageFormat parser and structural comparison
+ policy/ Stable translation-policy hashing
+ state/ Versioned translation manifest
+ styleguide/ Style guide loader
+ tm/ JSONL translation memory
+ translate/ Translation orchestrator
+ validate/ Locale validation and diffing
```
-
+
## เปรียบเทียบกับทางเลือกอื่น
| ฟีเจอร์ | Internationalizer | i18next | Crowdin | LLM ทั่วไป |
|---------|------------------|---------|---------|-------------|
-| การแปลด้วย LLM | ใช่ | ไม่ | บางส่วน | ใช่ |
-| คู่มือสไตล์แยกตามภาษา | ใช่ | ไม่ | ไม่ | ไม่ |
+| การแปลด้วยพลัง LLM | ใช่ | ไม่ | บางส่วน | ใช่ |
+| คู่มือสไตล์เฉพาะแต่ละภาษา | ใช่ | ไม่ | ไม่ | ไม่ |
| การบังคับใช้อภิธานศัพท์ | ใช่ | ไม่ | ใช่ | ไม่ |
| หน่วยความจำการแปล | ใช่ | ไม่ | ใช่ | ไม่ |
-| CLI / รันบนเครื่อง | ใช่ | N/A | ไม่ | ทำเอง |
-| ไฟล์ที่เป็นมิตรกับ Git | ใช่ | ใช่ | บางส่วน | ทำเอง |
-| ไม่พึ่งพา SaaS | ใช่ | ใช่ | ไม่ | แตกต่างกันไป |
+| ทำงานผ่าน CLI / บนเครื่อง | ใช่ | N/A | ไม่ | ทำเอง |
+| ไฟล์ที่เข้ากับ Git ได้ดี | ใช่ | ใช่ | บางส่วน | ทำเอง |
+| ไม่ต้องพึ่งพา SaaS | ใช่ | ใช่ | ไม่ | แตกต่างกันไป |
| โอเพนซอร์ส (AGPL-3.0) | ใช่ | ใช่ | ไม่ | แตกต่างกันไป |
+
+## สัญญาอนุญาต
-## ไลเซนส์ (License)
-
-[AGPL-3.0](LICENSE)
-
-## การมีส่วนร่วม (Contributing)
+[AGPL-3.0](../../LICENSE)
-ดู [CONTRIBUTING.md](CONTRIBUTING.md) สำหรับการตั้งค่าการพัฒนาและแนวทางปฏิบัติ การมีส่วนร่วมทั้งหมดจำเป็นต้องมีการลงนาม DCO (DCO sign-off)
+ดู [THIRD_PARTY_NOTICES.md](../../THIRD_PARTY_NOTICES.md) สำหรับประกาศเกี่ยวกับบุคคลที่สาม
+
+## การมีส่วนร่วม
+ดู [CONTRIBUTING.md](../../CONTRIBUTING.md) สำหรับการตั้งค่าสภาพแวดล้อมการพัฒนาและแนวทางปฏิบัติ การมีส่วนร่วมทั้งหมดจำเป็นต้องมีการลงนามรับรอง DCO (DCO sign-off)
diff --git a/docs/i18n/tr.md b/docs/i18n/tr.md
index 94a3a21..af849ad 100644
--- a/docs/i18n/tr.md
+++ b/docs/i18n/tr.md
@@ -6,32 +6,34 @@
# Internationalizer
-Yazılım projeleri için AI tabanlı uluslararasılaştırma (internationalization) ardışık düzeni. LLM'leri kullanarak i18n dosyalarını çevirin, doğrulayın ve yönetin.
+Yazılım projeleri için AI tabanlı uluslararasılaştırma ardışık düzeni. LLM'leri kullanarak i18n dosyalarını çevirin, doğrulayın ve yönetin.
[](https://github.com/Tom-R-Main/Internationalizer/actions/workflows/ci.yml)
-[](LICENSE)
+[](../../LICENSE)
-العربية · বাংলা · Deutsch · Español · Français · हिन्दी · Indonesia · 日本語 · ਪੰਜਾਬੀ · Português · Русский · తెలుగు
ไทย · Українська · 粵語 · 简体中文 · 繁體中文
+العربية · বাংলা · Čeština · Dansk · Deutsch · Ελληνικά · Español · Suomi · Français · עברית · हिन्दी · Indonesia · Italiano · 日本語 · 한국어 · Bahasa Melayu
Nederlands · ਪੰਜਾਬੀ · Polski · Português · Română · Русский · Svenska · తెలుగు · ไทย · Türkçe · Українська · Tiếng Việt · 粵語 · 简体中文 · 繁體中文
---
+
## Neden Internationalizer?
Çoğu i18n aracı ya çalışma zamanı kütüphaneleridir (i18next, react-intl) ya da anahtar yönetimi SaaS platformlarıdır (Crowdin, Lokalise). Hiçbiri asıl çeviri sorununu iyi bir şekilde çözmez:
- **Manuel çeviri** birkaç dilden sonra ölçeklenemez
- **Makine çevirisi API'leri** (Google Translate, DeepL) terminolojinizi, üslubunuzu ve kullanıcı arayüzü kurallarınızı göz ardı eder
-- **Genel LLM çevirisi** daha iyi çalışır, ancak sözlükler ve stil kılavuzları olmadan tutarsız sonuçlar elde edersiniz
+- **Genel LLM çevirisi** daha iyi sonuç verir; ancak sözlükler ve stil kılavuzları olmadan tutarsız sonuçlar elde edersiniz
Internationalizer farklıdır. LLM çevirisini şunlarla birleştiren bir **CLI ardışık düzenidir**:
- **Dile özel sözlükler** — uygulamanız genelinde tutarlı terminolojiyi zorunlu kılar
- **Dile özel stil kılavuzları** — üslubu, resmiyeti, çoğullaştırmayı ve tipografiyi kontrol eder
-- **Çeviri belleği** — değişmeyen metinleri atlar, API çağrılarında tasarruf sağlar
-- **Anahtar doğrulaması** — eksik çevirileri ve interpolasyon uyuşmazlıklarını yayına girmeden önce yakalar
+- **Çeviri belleği** — değişmeyen dizeleri atlar, API çağrılarından tasarruf sağlar
+- **Belirleyici doğrulama** — eksik veya fazladan anahtarları, korunan yapı kaymalarını, sözlük sorunlarını ve çoğul ya da ICU hatalarını yayına girmeden önce yakalar
+
## Kurulum
npm üzerinden kurun:
@@ -40,7 +42,7 @@ npm üzerinden kurun:
npm install -g internationalizer
```
-Veya global kurulum yapmadan çalıştırın:
+Veya genel bir kurulum yapmadan çalıştırın:
```bash
npx internationalizer --help
@@ -62,13 +64,15 @@ cd Internationalizer
go build -o internationalizer ./cmd/internationalizer
```
+
## npm paketleri
- Git etiketleri ve npm paket sürümleri eşleşmelidir, örneğin `v0.1.0` ve `0.1.0`
- Kök `internationalizer` paketi, `internationalizer-darwin-arm64` gibi platform paketlerine bağlıdır
- Desteklenen npm hedefleri: macOS arm64/x64, Linux arm64/x64, Windows x64
-- CI yayınlaması için `NPM_TOKEN` adında bir GitHub gizli anahtarı (secret) gerekir
+- CI yayınlaması için `NPM_TOKEN` adlı bir GitHub gizli dizisi (secret) gerekir
+
## Hızlı başlangıç
1. Projenizin kök dizininde bir yapılandırma dosyası oluşturun:
@@ -77,7 +81,11 @@ go build -o internationalizer ./cmd/internationalizer
# .internationalizer.yml
source_locale: en
target_locales: [fr, de, es, ja]
-source_path: locales/en.json
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
llm:
provider: gemini
@@ -103,36 +111,65 @@ internationalizer translate --dry-run
internationalizer translate
```
-5. Tüm yerel ayarları (locales) doğrulayın:
+5. Tüm yerel ayarları doğrulayın:
```bash
internationalizer validate
```
+
## Komutlar
### `translate`
-Eksik anahtarları bulun ve bir LLM aracılığıyla çevirin.
+Eksik veya güncelliğini yitirmiş anahtarları bulun ve bunları bir LLM aracılığıyla çevirin.
```bash
internationalizer translate # translate all locales
internationalizer translate -l fr # translate French only
internationalizer translate --dry-run # preview without API calls
+internationalizer translate --adopt-existing # baseline existing translations without API calls
+internationalizer translate --refresh-policy # refresh prompt/style/model-stale entries
internationalizer translate --batch-size 20 # smaller batches
internationalizer translate --concurrency 2 # fewer parallel calls
```
+Çeviri durumu; eksik, kaynak açısından eski, ilke açısından eski, güncel ve el ile düzenlenmiş durumları bağımsız olarak bildirir; böylece el ile yapılan bir düzenleme kaynak veya ilke değişikliğini gizleyemez. İlke açısından eski değerler bildirilir ancak yalnızca `--refresh-policy` ile yeniden çevrilir. El ile düzenlenen değerlerin üzerine hiçbir zaman otomatik olarak yazılmaz. Bildirimi (manifest) incelenmiş çevirilerle ilk kez tanıştırırken veya incelenmiş el ile düzenlemeyi açıkça yeni temel durum (baseline) olarak kabul ederken `--adopt-existing` seçeneğini kullanın.
+
### `validate`
-Tüm yerel ayar dosyalarını eksik anahtarlar, fazladan anahtarlar ve interpolasyon uyuşmazlıkları açısından kontrol edin.
+Tüm yerel ayar dosyalarını kaynak paketlerine göre denetleyin. Varsayılan doğrulama; yapısal kapsamı (gerekli hedef anahtarların mevcut olma yüzdesini) denetler, fazladan anahtarları uyarı olarak bildirir ve eksik anahtarlar, interpolasyon uyuşmazlıkları veya geçersiz ICU MessageFormat yapısı durumunda işlemi başarısız sayar.
```bash
internationalizer validate # human-readable output
internationalizer validate --json # machine-readable JSON
internationalizer validate -q # exit code only
+internationalizer validate --strict # enforce translation quality rules
+internationalizer validate --require-state # require current manifest provenance
```
+`--strict` aynı zamanda çevrilmiş kapsamı da bildirir. Kaynağıyla birebir aynı olan dilsel bir değer, sözlükte değerin tamamı için tam olarak aynı kaynak ve aynı hedefe sahip bir girdi açıkça yer almadıkça çevrilmemiş kabul edilir; `ignore_case` dikkate alınır, ancak daha uzun bir değerin içine gömülmüş bir sözlük terimi muafiyet sağlamaz. Katı mod (strict mode); fazladan anahtarlar, kaynakla aynı değerler, değişen interpolasyon/HTML/kod/Markdown bağlantı yapısı, sözlük ihlalleri ve yapılandırılmış çoğul biçimler durumunda başarısız olur.
+
+`--require-state`, her hedefi `.internationalizer.lock` dosyasına göre doğrular. Bir anahtar izlenmediğinde ya da kaydedilen kaynağı, çeviri ilkesi veya hedef karması (hash) güncelliğini yitirdiğinde başarısız olur. `--strict` ile birlikte kullanılabilir.
+
+Kullanıcı dostu ve JSON raporları kararlı bulgu kodları kullanır:
+
+| Kod | Anlamı |
+| --- | --- |
+| `missing_key` / `extra_key` | Kaynak ve hedef anahtar kümeleri farklı |
+| `blank_translation` | Boş olmayan bir kaynağın katı mod hedefi boş |
+| `source_identical` | Katı mod dilsel değeri çevrilmeden kalmış |
+| `protected_structure_mismatch` | İnterpolasyon, HTML, kod veya bağlantı yapısı değişmiş |
+| `glossary_violation` | Onaylanmış hiçbir hedef terim veya varyant bulunamadı |
+| `plural_form_missing` | Yapılandırılmış yerel ayar çoğul biçimi eksik |
+| `icu_message_syntax` | Kaynak veya hedef ICU iletisi bozuk |
+| `icu_argument_mismatch` | ICU bağımsız değişken adları, türleri veya biçimlendirici stilleri farklı |
+| `icu_selector_mismatch` | Seçiciler farklı veya bir çoğul kategorisi hedef yerel ayar için geçersiz |
+| `untracked` | Hedef için bildirim kaydı yok |
+| `source_stale` | Kaynak içerik, kaydedilen çeviriden sonra değişti |
+| `policy_stale` | Oluşturulan istem veya model ayarları değişti |
+| `target_modified` | Hedef içerik bildirim kaydından farklı |
+
### `detect`
i18n çerçevesini otomatik olarak algılayın ve bir yapılandırma önerin.
@@ -141,11 +178,11 @@ i18n çerçevesini otomatik olarak algılayın ve bir yapılandırma önerin.
internationalizer detect
```
-Desteklenenler: react-i18next, next-intl, vue-i18n, saf JSON, markdown belgeleri.
+Desteklenenler: react-i18next, next-intl, vue-i18n, yalın JSON, markdown belgeleri.
### `glossary`
-Çeviri sırasında zorunlu kılınan dile özel sözlük terimlerini yönetin.
+Çeviri sırasında zorunlu tutulan dile özel sözlük terimlerini yönetin.
```bash
internationalizer glossary list --locale fr
@@ -155,7 +192,7 @@ internationalizer glossary remove --locale fr --source "Dashboard"
### `tm`
-Çeviri belleğini (önceden çevrilmiş metinlerin JSONL önbelleği) yönetin.
+Çeviri belleğini (daha önce çevrilmiş dizelerin JSONL önbelleği) yönetin.
```bash
internationalizer tm stats # show record counts
@@ -163,6 +200,7 @@ internationalizer tm export # dump as JSON
internationalizer tm clear --force # delete all records
```
+
## Yapılandırma referansı
```yaml
@@ -172,10 +210,23 @@ internationalizer tm clear --force # delete all records
source_locale: en
# Languages to translate into (required)
-target_locales: [fr, de, es, ja, zh-CN, ar]
-
-# Path to the source locale file (required)
-source_path: locales/en.json
+target_locales: [fr, de, es, ja, yue, zh-CN, zh-TW, ar]
+
+# One or more source-to-target mappings (required).
+# {locale} is replaced with each configured target locale.
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
+ - id: docs
+ source: README.md
+ target: docs/i18n/{locale}.md
+ format: markdown
+
+# Backward compatibility: source_path still maps targets to sibling files
+# such as locales/fr.json. Prefer bundles for new projects.
+# source_path: locales/en.json
# LLM provider settings
llm:
@@ -184,7 +235,7 @@ llm:
# Model name defaults by provider:
# anthropic: claude-opus-5
- # openai: gpt-5.6-luna
+ # openai: gpt-5.6-luna (reasoning effort defaults to max)
# gemini: gemini-3.8-flash
# openrouter: deepseek/deepseek-v4-pro-0813
model: gemini-3.8-flash
@@ -195,6 +246,27 @@ llm:
# Base URL for OpenAI-compatible endpoints (optional)
# base_url: https://api.openai.com
+ # OpenAI GPT-5-series Responses API reasoning effort
+ # (default: max for the OpenAI provider)
+ reasoning_effort: max
+
+ # Optional LLM settings for individual target locales. An override using the
+ # global provider inherits unspecified global settings. A different provider
+ # uses that provider's defaults for unspecified settings.
+ locale_overrides:
+ yue:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-CN:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-TW:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+
# Keys per LLM call (default: 40)
batch_size: 40
@@ -209,11 +281,26 @@ glossary_dir: glossary
# Path to translation memory file (default: .internationalizer/tm.jsonl)
tm_path: .internationalizer/tm.jsonl
+
+# Versioned source, policy, target, and provenance state
+# (default: .internationalizer.lock; commit this file)
+manifest_path: .internationalizer.lock
+
+# Optional translation and strict-validation rules
+validation:
+ plural_style: i18next-v4 # generate and validate target-locale plural forms
```
+Yerel ayar tanımlayıcıları, `fr`, `pt-BR` veya `sr-Latn-RS` gibi düzgün biçimlendirilmiş BCP 47 etiketleri olmalıdır. Kurallı eşdeğer hedef yerel ayarlar yinelenen olarak reddedilir ve yerel ayara özel sağlayıcı geçersiz kılmaları kurallı eşdeğer yazımla eşleşir. Yukarıdaki örnekte, geçersiz kılma içermeyen yerel ayarlar —Japonca dahil— genel Gemini yapılandırmasını devralır.
+
+ICU MessageFormat değerleri yapısal olarak ayrıştırılır. Basit bağımsız değişkenler, `select`, `plural`, `selectordinal`, `number`, `date` ve `time`; iç içe geçmiş iletiler, çoğul uzaklıkları (offsets), tam sayı seçicileri ve `#` dahil olmak üzere desteklenir. Doğrulama; sözdizimini, bağımsız değişken türlerini ve biçimlendirici stillerini, çoğul uzaklıklarını, select dalı kimliğini ve hedef yerel ayarın CLDR çoğul kategorilerini denetler. Bu değişmezleri bozan sağlayıcı çıktısı, bir yerel ayar dosyası veya çeviri belleği kaydı yazılmadan önce reddedilir.
+
+`i18next-v4` ile, tanınan kaynak çoğul aileleri çeviri sırasında hedef yerel ayarın CLDR kategorilerine genişletilir. Yalnızca hedefte bulunan bir kategori, kaynak ailesinin `_other` değerini çeviri şablonu olarak kullanır. Katı doğrulama bu hedef kategorilerini zorunlu kılar; yalnızca kaynakta bulunan kategoriler ise bunları kullanmayan hedef yerel ayarlar için isteğe bağlıdır.
+
+
## Stil kılavuzları
-Stil kılavuzları, LLM çeviri istemine (prompt) enjekte edilen Markdown dosyalarıdır. Üslubu, resmiyeti, tipografiyi ve dile özgü diğer kuralları kontrol ederler.
+Stil kılavuzları, LLM çeviri istemine eklenen Markdown dosyalarıdır. Üslubu, resmiyeti, tipografiyi ve dile özgü diğer kuralları kontrol ederler.
```
style-guides/
@@ -223,60 +310,71 @@ style-guides/
ar.md # Arabic-specific rules
```
-### Ortak kurallar (`_conventions.md`)
+### Paylaşılan kurallar (`_conventions.md`)
-Tüm diller için geçerli olan kuralları tanımlayın: interpolasyon sözdizimi, HTML'in korunması, metin türü kuralları (butonlar, etiketler, hatalar) vb.
+Tüm diller için geçerli kuralları tanımlayın: interpolasyon sözdizimi, HTML'in korunması, dize türü kuralları (butonlar, etiketler, hatalar vb.).
### Dile özel kılavuzlar (`{locale}.md`)
-Dile özgü kuralları tanımlayın: resmiyet derecesi (sen/siz), noktalama işaretleri (açılı tırnaklar, ters soru işaretleri), çoğul biçimler, tarih/sayı biçimlendirmesi ve terminoloji sözlüğü.
+Dile özgü kuralları tanımlayın: resmiyet düzeyi (sen/siz), noktalama işaretleri (açılı tırnaklar, ters soru işaretleri), çoğul biçimler, tarih/sayı biçimlendirmesi ve terminoloji sözlüğü.
-Çalışan bir örnek için [`examples/react-app/style-guides/`](examples/react-app/style-guides/) dizinine bakın.
+Stil kılavuzları kalıcı ilke girdileridir, oluşturulan çıktılar değildir. Internationalizer bunları okur ancak asla üzerlerine yazmaz. İçerikleri sözlükten ve istem sözleşmesinden ayrı olarak karma işlemine tabi tutulur, bu nedenle bir uygulama kodu değişikliği çeviriyi geçersiz kılmaz. Bir kılavuzu düzenlemek, ilgili yerel ayarı kasıtlı olarak ilke incelemesi için işaretler; istem sözleşmesi sürümü değişmedikçe dahili istem metninin değiştirilmesi bunu tetiklemez.
-## Sözlük formatı
+Çalışan bir örnek için [`examples/react-app/style-guides/`](../../examples/react-app/style-guides/) dizinine bakın.
-Sözlük dosyaları, `{glossary_dir}/{locale}.json` içinde saklanan JSON dizileridir:
+
+## Sözlük biçimi
+
+Sözlük dosyaları, `{glossary_dir}/{locale}.json` yolunda saklanan JSON dizileridir:
```json
[
{
"source": "Dashboard",
"target": "Tableau de bord",
+ "variants": ["Panneau de contrôle"],
+ "enforcement": "error",
"ignore_case": false,
"whole_word": true
}
]
```
-Terimler, LLM istemine bir terminoloji tablosu olarak enjekte edilir ve uygulamanız genelinde önemli terimlerin tutarlı bir şekilde çevrilmesini sağlar.
+`variants`, onaylanmış diğer hedef biçimleri listeler. `enforcement`; `error`, `warning` olabilir veya varsayılan hata davranışı için belirtilmeyebilir. Terimler, LLM istemine bir terminoloji tablosu olarak eklenir ve uygulamanız genelinde tutarlı çeviri yapılmasını sağlar. `{"source":"API","target":"API"}` gibi tam bir girdi, aynı zamanda kaynakla birebir aynı olan bu değerin tamamını katı çevrilmemiş değer bulgularından muaf tutar; yalnızca `API` içeren daha uzun bir değeri muaf tutmaz.
+
## Çeviri belleği
-Çeviri belleği bir JSONL dosyası olarak saklanır (her satırda bir JSON kaydı). Her kayıt şunları içerir:
+Çeviri belleği bir JSONL dosyası olarak saklanır (satır başına bir JSON kaydı). Her kayıt şunları içerir:
-- Kaynak anahtar ve değer
-- Çevrilen değer
-- Kaynak değerin SHA-256 özeti (hash)
-- Zaman damgası
+- Paket, anahtar, kaynak değer, çevrilmiş değer ve kurallı hedef yerel ayar
+- Kaynak, stil kılavuzu, sözlük, istem sözleşmesi ve birleşik ilke karmaları
+- Çeviriyi üreten sağlayıcı ve model
+- Bir zaman damgası
-Sonraki çalıştırmalarda, değişmeyen metinler LLM çağrılmadan TM önbelleğinden sunulur; bu da hem zamandan hem de API maliyetlerinden tasarruf sağlar. TM dosyası git dostudur ve yerel ayar dosyalarınızla birlikte işlenebilir (commit).
+Sonraki çalıştırmalarda, aynı kaynak ve ilke karmalarına sahip dizeler LLM çağrılmadan önbellekten sunulur. Varsayılan yol yoksayılan `.internationalizer/` dizini altındadır, bu nedenle yerel bir önbellek olarak kalır. Projeniz çeviri belleğini kasıtlı olarak paylaşıyorsa `tm_path` değerini izlenen bir konuma ayarlayın. İncelenebilir `.internationalizer.lock` bildirimi ayrı olarak sürümlendirilir.
-## Desteklenen formatlar
+
+## Desteklenen biçimler
-| Format | Uzantılar | Mod |
-|--------|-----------|------|
-| JSON | `.json` | Anahtar-değer (iç içe, nokta notasyonuyla düzleştirilmiş) |
+| Biçim | Uzantılar | Mod |
+| --- | --- | --- |
+| JSON | `.json` | Anahtar-değer (iç içe, nokta gösterimiyle düzleştirilmiş) |
| YAML | `.yml`, `.yaml` | Anahtar-değer (yorumları ve sıralamayı korur) |
-| Markdown | `.md`, `.mdx` | Tüm belge çevirisi |
+| Markdown | `.md`, `.mdx` | Başlangıç metni (preamble) ve H2 düzeyinde bölümler |
+
+Markdown hedefleri, H2 bölümlerinden önce görünmeyen `internationalizer:unit` yorumları içerir. Bu kararlı işaretleyiciler, Internationalizer'ın ilişkisiz bölümleri yeniden çevirmeden tek bir kaynak bölümü eklemesine, taşımasına veya düzenlemesine olanak tanır. İşaretsiz mevcut belgeler, bir sonraki başarılı güncellemelerinde bu işaretleyicileri alır.
+
## Proje türü algılama
-`internationalizer detect` aşağıdakileri kontrol ederek i18n kurulumunuzu tanımlar:
+`internationalizer detect` şunları denetleyerek i18n kurulumunuzu tanımlar:
- react-i18next, next-intl veya vue-i18n için `package.json` bağımlılıkları
- Yaygın yerel ayar desenleriyle eşleşen dizin yapıları
-- Dosya uzantıları ve isimlendirme kuralları
+- Dosya uzantıları ve adlandırma kuralları
+
## Mimari
```
@@ -291,30 +389,38 @@ internal/
openai.go OpenAI / compatible backend
gemini.go Google Gemini via AI Studio backend
OpenRouter uses openai.go with custom base_url
+ locale/ BCP 47 identity and CLDR plural categories
+ message/ ICU MessageFormat parser and structural comparison
+ policy/ Stable translation-policy hashing
+ state/ Versioned translation manifest
styleguide/ Style guide loader
tm/ JSONL translation memory
translate/ Translation orchestrator
validate/ Locale validation and diffing
```
+
## Alternatiflerle karşılaştırma
| Özellik | Internationalizer | i18next | Crowdin | Genel LLM |
-|---------|------------------|---------|---------|-------------|
+| --- | --- | --- | --- | --- |
| LLM destekli çeviri | Evet | Hayır | Kısmen | Evet |
| Dile özel stil kılavuzları | Evet | Hayır | Hayır | Hayır |
| Sözlük zorunluluğu | Evet | Hayır | Evet | Hayır |
| Çeviri belleği | Evet | Hayır | Evet | Hayır |
-| CLI / yerel yürütme | Evet | N/A | Hayır | Manuel |
+| CLI / yerel yürütme | Evet | Yok | Hayır | Manuel |
| Git dostu dosyalar | Evet | Evet | Kısmen | Manuel |
| SaaS bağımlılığı yok | Evet | Evet | Hayır | Değişir |
| Açık kaynak (AGPL-3.0) | Evet | Evet | Hayır | Değişir |
+
## Lisans
-[AGPL-3.0](LICENSE)
+[AGPL-3.0](../../LICENSE)
-## Katkıda bulunma
+Bağımlılık bildirimleri için [THIRD_PARTY_NOTICES.md](../../THIRD_PARTY_NOTICES.md) dosyasına bakın.
-Geliştirme kurulumu ve yönergeler için [CONTRIBUTING.md](CONTRIBUTING.md) dosyasına bakın. Tüm katkılar DCO onayı gerektirir.
+
+## Katkıda bulunma
+Geliştirme kurulumu ve yönergeler için [CONTRIBUTING.md](../../CONTRIBUTING.md) dosyasına bakın. Tüm katkılar için DCO onayı (sign-off) gereklidir.
diff --git a/docs/i18n/uk.md b/docs/i18n/uk.md
index b3db2cc..3919e4b 100644
--- a/docs/i18n/uk.md
+++ b/docs/i18n/uk.md
@@ -9,46 +9,54 @@
AI-нативний конвеєр інтернаціоналізації для програмних проєктів. Перекладайте, перевіряйте та керуйте файлами i18n за допомогою LLM.
[](https://github.com/Tom-R-Main/Internationalizer/actions/workflows/ci.yml)
-[](LICENSE)
+[](../../LICENSE)
+
+العربية · বাংলা · Čeština · Dansk · Deutsch · Ελληνικά · Español · Suomi · Français · עברית · हिन्दी · Indonesia · Italiano · 日本語 · 한국어 · Bahasa Melayu
Nederlands · ਪੰਜਾਬੀ · Polski · Português · Română · Русский · Svenska · తెలుగు · ไทย · Türkçe · Українська · Tiếng Việt · 粵語 · 简体中文 · 繁體中文
+
+
+---
+
+
## Чому Internationalizer?
-Більшість інструментів i18n — це або бібліотеки часу виконання (i18next, react-intl), або SaaS-платформи для керування ключами (Crowdin, Lokalise). Жоден із них не вирішує саму проблему перекладу належним чином:
+Більшість інструментів i18n — це або бібліотеки часу виконання (i18next, react-intl), або SaaS-платформи для керування ключами (Crowdin, Lokalise). Жоден із них не розв'язує належним чином саме завдання перекладу:
-- **Ручний переклад** не масштабується більше ніж на кілька мов
-- **API машинного перекладу** (Google Translate, DeepL) ігнорують вашу термінологію, тон та угоди щодо інтерфейсу користувача
-- **Звичайний переклад за допомогою LLM** працює краще, але без глосаріїв та посібників зі стилю ви отримуєте непослідовні результати
+- **Ручний переклад** не масштабується, якщо мов більше кількох
+- **API машинного перекладу** (Google Translate, DeepL) ігнорують вашу термінологію, тон і конвенції інтерфейсу користувача
+- **Звичайний переклад за допомогою LLM** працює краще, але без глосаріїв і посібників зі стилю дає неузгоджені результати
-Internationalizer відрізняється. Це **CLI-конвеєр**, який поєднує переклад за допомогою LLM із:
+Internationalizer влаштований інакше. Це **конвеєр CLI**, що поєднує переклад за допомогою LLM із такими можливостями:
-- **Глосаріями для кожної мови** — забезпечують узгоджену термінологію у всьому вашому застосунку
-- **Посібниками зі стилю для кожної мови** — контролюють тон, формальність, множину та типографіку
-- **Пам'яттю перекладів (Translation memory)** — пропускає незмінені рядки, заощаджуючи гроші на викликах API
-- **Перевіркою ключів** — виявляє відсутні переклади та невідповідності інтерполяції до релізу
+- **Глосарії для окремих мов** — забезпечують узгоджену термінологію в усьому застосунку
+- **Посібники зі стилю для окремих мов** — контролюють тон, формальність, форми множини й типографіку
+- **Пам'ять перекладів** — пропускає незмінені рядки, заощаджуючи кошти на викликах API
+- **Детермінована валідація** — виявляє відсутні чи зайві ключі, порушення захищеної структури, невідповідності глосарію, а також помилки у формах множини або ICU ще до релізу
+
## Встановлення
-Встановіть через npm:
+Встановлення з npm:
```bash
npm install -g internationalizer
```
-Або запустіть без глобального встановлення:
+Або запуск без глобального встановлення:
```bash
npx internationalizer --help
```
-Пакет npm встановлює відповідний попередньо зібраний бінарний файл з npm через специфічні для платформи необов'язкові залежності.
+Пакет npm встановлює відповідний скомпільований бінарний файл з npm через платформні необов'язкові залежності.
-Встановіть за допомогою Go:
+Встановлення за допомогою Go:
```bash
go install github.com/Tom-R-Main/Internationalizer/cmd/internationalizer@latest
```
-Або зберіть із вихідного коду:
+Або збирання з вихідного коду:
```bash
git clone https://github.com/Tom-R-Main/Internationalizer.git
@@ -56,22 +64,28 @@ cd Internationalizer
go build -o internationalizer ./cmd/internationalizer
```
+
## Пакети npm
-- Теги Git та версії пакетів npm мають збігатися, наприклад `v0.1.0` та `0.1.0`
-- Кореневий пакет `internationalizer` залежить від пакетів платформи, таких як `internationalizer-darwin-arm64`
+- Теги Git і версії пакетів npm мають збігатися, наприклад `v0.1.0` та `0.1.0`
+- Кореневий пакет `internationalizer` залежить від платформних пакетів, таких як `internationalizer-darwin-arm64`
- Підтримувані цільові платформи npm: macOS arm64/x64, Linux arm64/x64, Windows x64
-- Для публікації через CI потрібен секрет GitHub із назвою `NPM_TOKEN`
+- Для публікації через CI потрібен секрет GitHub з назвою `NPM_TOKEN`
-## Швидкий старт
+
+## Швидкий початок
-1. Створіть файл конфігурації в корені вашого проєкту:
+1. Створіть конфігураційний файл у корені вашого проєкту:
```yaml
# .internationalizer.yml
source_locale: en
target_locales: [fr, de, es, ja]
-source_path: locales/en.json
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
llm:
provider: gemini
@@ -79,13 +93,13 @@ llm:
api_key_env: GOOGLE_AI_STUDIO_API_KEY
```
-2. Встановіть ваш API-ключ:
+2. Задайте свій ключ API:
```bash
export GOOGLE_AI_STUDIO_API_KEY=your-ai-studio-key
```
-3. Попередній перегляд того, що буде перекладено:
+3. Перегляньте попередній список рядків для перекладу:
```bash
internationalizer translate --dry-run
@@ -103,43 +117,72 @@ internationalizer translate
internationalizer validate
```
+
## Команди
### `translate`
-Знаходить відсутні ключі та перекладає їх за допомогою LLM.
+Знайти відсутні або застарілі ключі та перекласти їх за допомогою LLM.
```bash
-internationalizer translate # перекласти всі локалі
-internationalizer translate -l fr # перекласти лише французьку
-internationalizer translate --dry-run # попередній перегляд без викликів API
-internationalizer translate --batch-size 20 # менші пакети
-internationalizer translate --concurrency 2 # менше паралельних викликів
+internationalizer translate # translate all locales
+internationalizer translate -l fr # translate French only
+internationalizer translate --dry-run # preview without API calls
+internationalizer translate --adopt-existing # baseline existing translations without API calls
+internationalizer translate --refresh-policy # refresh prompt/style/model-stale entries
+internationalizer translate --batch-size 20 # smaller batches
+internationalizer translate --concurrency 2 # fewer parallel calls
```
+Стан перекладу незалежно фіксує відсутні (missing), застарілі через зміну джерела (source-stale), застарілі через зміну політики (policy-stale), актуальні (current) та відредаговані вручну (manually edited) умови, тому ручне редагування не приховає зміну вихідного тексту чи політики. Значення із застарілою політикою відображаються у звіті, але повторно перекладаються лише з параметром `--refresh-policy`. Змінені вручну значення ніколи не перезаписуються автоматично. Використовуйте `--adopt-existing` під час першого підключення маніфесту до вже перевірених перекладів або щоб явно затвердити перевірене ручне редагування як новий базовий стан.
+
### `validate`
-Перевіряє всі файли локалей на наявність відсутніх ключів, зайвих ключів та невідповідностей інтерполяції.
+Перевірити всі файли локалей на відповідність вихідним бандлам. За замовчуванням валідація перевіряє структурне покриття (відсоток наявних обов'язкових цільових ключів), повідомляє про зайві ключі як про попередження та завершується помилкою в разі відсутності ключів, невідповідності інтерполяції чи некоректної структури ICU MessageFormat.
```bash
-internationalizer validate # зручний для читання вивід
-internationalizer validate --json # машинозчитуваний JSON
-internationalizer validate -q # лише код завершення
+internationalizer validate # human-readable output
+internationalizer validate --json # machine-readable JSON
+internationalizer validate -q # exit code only
+internationalizer validate --strict # enforce translation quality rules
+internationalizer validate --require-state # require current manifest provenance
```
+Параметр `--strict` також звітує про якісне покриття перекладу. Лінгвістичне значення, ідентичне до вихідного, вважається неперекладеним, якщо тільки глосарій явно не містить точного запису з однаковим джерелом і цільовим значенням для всього рядка; параметр `ignore_case` враховується, але входження терміна з глосарія у довший рядок не звільняє його від перекладу. Суворий режим завершується помилкою за наявності зайвих ключів, значень, ідентичних до вихідних, зміненої структури інтерполяції, HTML, коду чи посилань Markdown, порушень глосарія та невідповідностей налаштованим формам множини.
+
+Параметр `--require-state` перевіряє кожен цільовий переклад за файлом `.internationalizer.lock`. Він завершується помилкою, якщо ключ не відстежується або якщо зафіксований для нього хеш вихідного тексту, політики перекладу чи цільового значення застарів. Його можна поєднувати з `--strict`.
+
+У звітах для користувача та у форматі JSON застосовуються стабільні коди проблем:
+
+| Код | Значення |
+| --- | --- |
+| `missing_key` / `extra_key` | Набори ключів у вихідному та цільовому файлах різняться |
+| `blank_translation` | Непорожнє вихідне значення має порожній цільовий переклад у суворому режимі |
+| `source_identical` | У суворому режимі лінгвістичне значення залишилося неперекладеним |
+| `protected_structure_mismatch` | Змінено структуру інтерполяції, HTML, коду або посилань |
+| `glossary_violation` | Затвердженого цільового терміна або його варіанта не знайдено |
+| `plural_form_missing` | Відсутня налаштована форма множини для цільової локалі |
+| `icu_message_syntax` | Помилка синтаксису повідомлення ICU у вихідному або цільовому рядку |
+| `icu_argument_mismatch` | Відрізняються імена аргументів ICU, їхні типи або стилі форматування |
+| `icu_selector_mismatch` | Селектори різняться або категорія множини є неприпустимою для цільової локалі |
+| `untracked` | У маніфесті відсутній запис для цільового елемента |
+| `source_stale` | Вихідний вміст змінився після зафіксованого перекладу |
+| `policy_stale` | Змінилися згенерований промпт або налаштування моделі |
+| `target_modified` | Вміст цільового перекладу відрізняється від запису в маніфесті |
+
### `detect`
-Автоматично визначає фреймворк i18n та пропонує конфігурацію.
+Автоматично визначити фреймворк i18n і запропонувати конфігурацію.
```bash
internationalizer detect
```
-Підтримує: react-i18next, next-intl, vue-i18n, звичайний JSON, документацію у Markdown.
+Підтримуються: react-i18next, next-intl, vue-i18n, стандартний JSON, документація у форматі Markdown.
### `glossary`
-Керує термінами глосарія для кожної мови, які застосовуються під час перекладу.
+Керування термінами глосарія для окремих мов, дотримання яких забезпечується під час перекладу.
```bash
internationalizer glossary list --locale fr
@@ -149,166 +192,235 @@ internationalizer glossary remove --locale fr --source "Dashboard"
### `tm`
-Керує пам'яттю перекладів (кеш JSONL попередньо перекладених рядків).
+Керування пам'яттю перекладів (кеш раніше перекладених рядків у форматі JSONL).
```bash
-internationalizer tm stats # показати кількість записів
-internationalizer tm export # експортувати як JSON
-internationalizer tm clear --force # видалити всі записи
+internationalizer tm stats # show record counts
+internationalizer tm export # dump as JSON
+internationalizer tm clear --force # delete all records
```
-## Довідник з конфігурації
+
+## Довідник із конфігурації
```yaml
# .internationalizer.yml
-# Вихідна мова (за замовчуванням: en)
+# Source language (default: en)
source_locale: en
-# Мови для перекладу (обов'язково)
-target_locales: [fr, de, es, ja, zh-CN, ar]
-
-# Шлях до файлу вихідної локалі (обов'язково)
-source_path: locales/en.json
-
-# Налаштування провайдера LLM
+# Languages to translate into (required)
+target_locales: [fr, de, es, ja, yue, zh-CN, zh-TW, ar]
+
+# One or more source-to-target mappings (required).
+# {locale} is replaced with each configured target locale.
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
+ - id: docs
+ source: README.md
+ target: docs/i18n/{locale}.md
+ format: markdown
+
+# Backward compatibility: source_path still maps targets to sibling files
+# such as locales/fr.json. Prefer bundles for new projects.
+# source_path: locales/en.json
+
+# LLM provider settings
llm:
- # Провайдер: "anthropic", "openai", "gemini" або "openrouter" (за замовчуванням: gemini)
+ # Provider: "anthropic", "openai", "gemini", or "openrouter" (default: gemini)
provider: gemini
- # Назви моделей за замовчуванням для провайдерів:
+ # Model name defaults by provider:
# anthropic: claude-opus-5
- # openai: gpt-5.6-luna
+ # openai: gpt-5.6-luna (reasoning effort defaults to max)
# gemini: gemini-3.8-flash
# openrouter: deepseek/deepseek-v4-pro-0813
model: gemini-3.8-flash
- # Змінна середовища, що містить API-ключ
+ # Environment variable containing the API key
api_key_env: GOOGLE_AI_STUDIO_API_KEY
- # Базова URL-адреса для сумісних з OpenAI кінцевих точок (необов'язково)
+ # Base URL for OpenAI-compatible endpoints (optional)
# base_url: https://api.openai.com
-# Кількість ключів на один виклик LLM (за замовчуванням: 40)
+ # OpenAI GPT-5-series Responses API reasoning effort
+ # (default: max for the OpenAI provider)
+ reasoning_effort: max
+
+ # Optional LLM settings for individual target locales. An override using the
+ # global provider inherits unspecified global settings. A different provider
+ # uses that provider's defaults for unspecified settings.
+ locale_overrides:
+ yue:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-CN:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-TW:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+
+# Keys per LLM call (default: 40)
batch_size: 40
-# Паралельні виклики LLM (за замовчуванням: 4)
+# Parallel LLM calls (default: 4)
concurrency: 4
-# Каталог, що містить файли Markdown посібників зі стилю для кожної локалі (за замовчуванням: style-guides)
+# Directory containing per-locale style guide Markdown files (default: style-guides)
style_guides_dir: style-guides
-# Каталог, що містить файли JSON глосаріїв для кожної локалі (за замовчуванням: glossary)
+# Directory containing per-locale glossary JSON files (default: glossary)
glossary_dir: glossary
-# Шлях до файлу пам'яті перекладів (за замовчуванням: .internationalizer/tm.jsonl)
+# Path to translation memory file (default: .internationalizer/tm.jsonl)
tm_path: .internationalizer/tm.jsonl
+
+# Versioned source, policy, target, and provenance state
+# (default: .internationalizer.lock; commit this file)
+manifest_path: .internationalizer.lock
+
+# Optional translation and strict-validation rules
+validation:
+ plural_style: i18next-v4 # generate and validate target-locale plural forms
```
+Ідентифікатори локалей мають бути коректними тегами BCP 47, такими як `fr`, `pt-BR` або `sr-Latn-RS`. Канонічно еквівалентні цільові локалі відхиляються як дублікати, а перевизначення провайдера для окремих локалей зіставляється за канонічно еквівалентним написанням. У наведеному вище прикладі локалі без перевизначення (зокрема японська) успадковують глобальну конфігурацію Gemini.
+
+Значення ICU MessageFormat аналізуються структурно. Підтримуються прості аргументи, `select`, `plural`, `selectordinal`, `number`, `date` і `time`, зокрема вкладені повідомлення, зміщення множини (plural offsets), селектори точних чисел та `#`. Валідація перевіряє синтаксис, типи аргументів і стилі форматування, зміщення множини, збіг гілок select та категорії множини CLDR цільової локалі. Відповіді провайдера, які порушують ці інваріанти, відхиляються до запису у файл локалі або в пам'ять перекладів.
+
+За використання `i18next-v4` розпізнані групи форм множини вихідної мови розгортаються під час перекладу у відповідні категорії CLDR цільової локалі. Якщо категорія наявна лише в цільовій мові, як шаблон перекладу використовується значення `_other` вихідної групи. Сувора валідація вимагає наявності таких цільових категорій; категорії, властиві лише вихідній мові, є необов'язковими для цільових локалей, де вони не використовуються.
+
+
## Посібники зі стилю
-Посібники зі стилю — це файли Markdown, які додаються до промпту перекладу LLM. Вони контролюють тон, формальність, типографіку та інші специфічні для мови угоди.
+Посібники зі стилю — це файли Markdown, які додаються до промпту перекладу для LLM. Вони контролюють тон, формальність, типографіку та інші мовні конвенції.
```
style-guides/
- _conventions.md # спільні правила для всіх мов
- fr.md # специфічні правила для французької
- ja.md # специфічні правила для японської
- ar.md # специфічні правила для арабської
+ _conventions.md # shared rules for all languages
+ fr.md # French-specific rules
+ ja.md # Japanese-specific rules
+ ar.md # Arabic-specific rules
```
-### Спільні угоди (`_conventions.md`)
+### Спільні конвенції (`_conventions.md`)
-Визначають правила, які застосовуються до всіх мов: синтаксис інтерполяції, збереження HTML, угоди щодо типів рядків (кнопки, мітки, помилки тощо).
+Визначають правила, що застосовуються до всіх мов: синтаксис інтерполяції, збереження HTML, конвенції щодо типів рядків (кнопки, мітки, помилки тощо).
-### Посібники для кожної мови (`{locale}.md`)
+### Посібники для окремих мов (`{locale}.md`)
-Визначають специфічні для мови правила: регістр формальності (tu чи vous), пунктуація (лапки-ялинки, перевернуті знаки питання), форми множини, форматування дат/чисел та глосарій термінології.
+Визначають специфічні для мови правила: регістр формальності (наприклад, звернення на «ти» чи «ви»), пунктуацію («лапки-ялинки», перевернуті знаки питання), форми множини, форматування дат і чисел, а також глосарій термінології.
-Дивіться [`examples/react-app/style-guides/`](examples/react-app/style-guides/) для робочого прикладу.
+Посібники зі стилю є сталими вхідними правилами політики, а не згенерованим результатом. Internationalizer читає їх, але ніколи не змінює. Їхній вміст хешується окремо від глосарія та контракту промпту, тому зміна коду застосунку не робить переклад застарілим. Редагування посібника зумисно позначає цю локаль як таку, що потребує перегляду політики; зміна внутрішніх формулювань промпту до цього не призводить, якщо не змінилася версія контракту промпту.
+Практичний приклад див. у каталозі [`examples/react-app/style-guides/`](../../examples/react-app/style-guides/).
+
+
## Формат глосарія
-Файли глосарія — це масиви JSON, що зберігаються у `{glossary_dir}/{locale}.json`:
+Файли глосаріїв — це масиви JSON, які зберігаються за шляхом `{glossary_dir}/{locale}.json`:
```json
[
{
"source": "Dashboard",
"target": "Tableau de bord",
+ "variants": ["Panneau de contrôle"],
+ "enforcement": "error",
"ignore_case": false,
"whole_word": true
}
]
```
-Терміни додаються до промпту LLM у вигляді таблиці термінології, що забезпечує узгоджений переклад ключових термінів у всьому вашому застосунку.
+Поле `variants` перелічує інші затверджені цільові форми. `enforcement` може приймати значення `error`, `warning` або бути пропущеним (за замовчуванням діє поведінка помилки). Терміни додаються в промпт для LLM у вигляді таблиці термінології, що гарантує послідовний переклад у всьому застосунку. Точний запис на кшталт `{"source":"API","target":"API"}` також звільняє це повне ідентичне до джерела значення від зауважень суворого режиму щодо неперекладених значень; він не звільняє довший рядок, який просто містить `API`.
+
## Пам'ять перекладів
-Пам'ять перекладів зберігається як файл JSONL (один запис JSON на рядок). Кожен запис містить:
+Пам'ять перекладів зберігається у вигляді файлу JSONL (один запис JSON на рядок). Кожен запис містить:
-- Вихідний ключ та значення
-- Перекладене значення
-- Хеш SHA-256 вихідного значення
-- Часову мітку
+- Бандл, ключ, вихідне значення, перекладене значення та канонічну цільову локаль
+- Хеші вихідного вмісту, посібника зі стилю, глосарія, контракту промпту та об'єднаної політики
+- Назву провайдера й моделі, за допомогою яких створено переклад
+- Позначку часу
-Під час наступних запусків незмінені рядки беруться з кешу TM без виклику LLM, що економить час і витрати на API. Файл TM є дружнім до git і може бути закомічений разом із вашими файлами локалей.
+Під час наступних запусків рядки з однаковими хешами вихідного тексту та політики беруться з кешу без звернення до LLM. За замовчуванням файл розміщується в ігнорованому каталозі `.internationalizer/`, тому залишається локальним кешем. Вкажіть для `tm_path` шлях, що відстежується системою контролю версій, якщо у вашому проєкті передбачено спільне використання пам'яті перекладів. Маніфест `.internationalizer.lock`, призначений для код-рев'ю, версіонується окремо.
+
## Підтримувані формати
| Формат | Розширення | Режим |
-|--------|-----------|------|
-| JSON | `.json` | Ключ-значення (вкладені, зведені за допомогою крапкової нотації) |
-| YAML | `.yml`, `.yaml` | Ключ-значення (зберігає коментарі та порядок) |
-| Markdown | `.md`, `.mdx` | Переклад усього документа |
+|---|---|---|
+| JSON | `.json` | «Ключ — значення» (вкладені структури, розгортання через крапкову нотацію) |
+| YAML | `.yml`, `.yaml` | «Ключ — значення» (збереження коментарів і порядку) |
+| Markdown | `.md`, `.mdx` | Преамбула та розділи рівня H2 |
+
+Цільові файли Markdown містять невидимі коментарі `internationalizer:unit` перед розділами рівня H2. Завдяки цим стабільним маркерам Internationalizer може додавати, переміщувати або редагувати окремий розділ вихідного тексту без повторного перекладу незв'язаних розділів. Документи, у яких маркерів ще немає, отримують їх під час наступного успішного оновлення.
+
## Визначення типу проєкту
-`internationalizer detect` ідентифікує ваші налаштування i18n шляхом перевірки:
+Команда `internationalizer detect` визначає конфігурацію i18n за такими ознаками:
-- Залежностей `package.json` для react-i18next, next-intl або vue-i18n
-- Структур каталогів, що відповідають загальним шаблонам локалей
-- Розширень файлів та угод щодо найменування
+- залежності в `package.json` на наявність react-i18next, next-intl або vue-i18n;
+- структура директорій, що відповідає поширеним шаблонам розміщення локалей;
+- розширення файлів і правила їх найменування.
+
## Архітектура
```
-cmd/internationalizer/ Точка входу CLI та визначення команд
+cmd/internationalizer/ CLI entry point and command definitions
internal/
- config/ Завантаження конфігурації YAML зі значеннями за замовчуванням
- detect/ Автоматичне визначення типу проєкту
- formats/ Парсери форматів (JSON, YAML, Markdown)
- glossary/ Керування глосарієм для кожної локалі
- llm/ Інтерфейс провайдера LLM + реалізації
- anthropic.go Бекенд Anthropic Claude
- openai.go Бекенд OpenAI / сумісний
- gemini.go Бекенд Google Gemini через AI Studio
- OpenRouter використовує openai.go з власним base_url
- styleguide/ Завантажувач посібника зі стилю
- tm/ Пам'ять перекладів JSONL
- translate/ Оркестратор перекладу
- validate/ Перевірка локалей та порівняння
+ config/ YAML config loading with defaults
+ detect/ Project type auto-detection
+ formats/ Format parsers (JSON, YAML, Markdown)
+ glossary/ Per-locale glossary management
+ llm/ LLM provider interface + implementations
+ anthropic.go Anthropic Claude backend
+ openai.go OpenAI / compatible backend
+ gemini.go Google Gemini via AI Studio backend
+ OpenRouter uses openai.go with custom base_url
+ locale/ BCP 47 identity and CLDR plural categories
+ message/ ICU MessageFormat parser and structural comparison
+ policy/ Stable translation-policy hashing
+ state/ Versioned translation manifest
+ styleguide/ Style guide loader
+ tm/ JSONL translation memory
+ translate/ Translation orchestrator
+ validate/ Locale validation and diffing
```
+
## Порівняння з альтернативами
-| Функція | Internationalizer | i18next | Crowdin | Звичайний LLM |
-|---------|------------------|---------|---------|-------------|
+| Можливість | Internationalizer | i18next | Crowdin | Звичайні LLM |
+|---|---|---|---|---|
| Переклад за допомогою LLM | Так | Ні | Частково | Так |
-| Посібники зі стилю для кожної мови | Так | Ні | Ні | Ні |
+| Посібники зі стилю для окремих мов | Так | Ні | Ні | Ні |
| Застосування глосарія | Так | Ні | Так | Ні |
| Пам'ять перекладів | Так | Ні | Так | Ні |
-| CLI / локальне виконання | Так | Н/Д | Ні | Вручну |
-| Дружні до Git файли | Так | Так | Частково | Вручну |
-| Відсутність залежності від SaaS | Так | Так | Ні | Варіюється |
-| Відкритий вихідний код (AGPL-3.0) | Так | Так | Ні | Варіюється |
+| CLI / локальний запуск | Так | Н/Д | Ні | Вручну |
+| Зручні для Git файли | Так | Так | Частково | Вручну |
+| Без залежності від SaaS | Так | Так | Ні | Різниться |
+| Відкритий вихідний код (AGPL-3.0) | Так | Так | Ні | Різниться |
+
## Ліцензія
-[AGPL-3.0](LICENSE)
+[AGPL-3.0](../../LICENSE)
-## Внесок
+Відомості про сторонні залежності див. у файлі [THIRD_PARTY_NOTICES.md](../../THIRD_PARTY_NOTICES.md).
-Дивіться [CONTRIBUTING.md](CONTRIBUTING.md) для отримання інструкцій з налаштування середовища розробки та правил. Усі внески вимагають підписання DCO.
+
+## Внесок у проєкт
+Інструкції щодо налаштування середовища розробки та вказівки див. у файлі [CONTRIBUTING.md](../../CONTRIBUTING.md). Усі внески потребують підписання DCO.
diff --git a/docs/i18n/vi.md b/docs/i18n/vi.md
index da797c5..c0db956 100644
--- a/docs/i18n/vi.md
+++ b/docs/i18n/vi.md
@@ -6,33 +6,34 @@
# Internationalizer
-Pipeline internationalization (i18n) gốc AI dành cho các dự án phần mềm. Dịch, xác thực và quản lý các tệp i18n bằng LLM.
+Pipeline quốc tế hóa (i18n) native AI cho các dự án phần mềm. Dịch, xác thực và quản lý các tệp i18n bằng LLM.
[](https://github.com/Tom-R-Main/Internationalizer/actions/workflows/ci.yml)
-[](LICENSE)
+[](../../LICENSE)
-العربية · বাংলা · Deutsch · Español · Français · हिन्दी · Indonesia · 日本語 · ਪੰਜਾਬੀ · Português · Русский · తెలుగు
-ไทย · Українська · 粵語 · 简体中文 · 繁體中文
+العربية · বাংলা · Čeština · Dansk · Deutsch · Ελληνικά · Español · Suomi · Français · עברית · हिन्दी · Indonesia · Italiano · 日本語 · 한국어 · Bahasa Melayu
Nederlands · ਪੰਜਾਬੀ · Polski · Português · Română · Русский · Svenska · తెలుగు · ไทย · Türkçe · Українська · Tiếng Việt · 粵語 · 简体中文 · 繁體中文
---
+
## Tại sao chọn Internationalizer?
-Hầu hết các công cụ i18n đều là thư viện runtime (i18next, react-intl) hoặc nền tảng SaaS quản lý key (Crowdin, Lokalise). Không có công cụ nào giải quyết tốt vấn đề dịch thuật thực sự:
+Hầu hết các công cụ i18n đều là thư viện runtime (i18next, react-intl) hoặc nền tảng SaaS quản lý khóa (Crowdin, Lokalise). Không có công cụ nào giải quyết tốt vấn đề dịch thuật thực tế:
-- **Dịch thủ công** không thể mở rộng khi có nhiều ngôn ngữ
-- **API dịch máy** (Google Translate, DeepL) bỏ qua thuật ngữ, giọng văn và quy ước UI của bạn
-- **Dịch bằng LLM thông thường** hoạt động tốt hơn, nhưng nếu không có bảng thuật ngữ và hướng dẫn văn phong, bạn sẽ nhận được kết quả không nhất quán
+- **Dịch thủ công** không thể mở rộng quy mô quá một vài ngôn ngữ
+- **API dịch máy** (Google Translate, DeepL) bỏ qua thuật ngữ, giọng văn và các quy ước UI của bạn
+- **Dịch bằng LLM thông thường** hoạt động tốt hơn, nhưng nếu không có bảng thuật ngữ và hướng dẫn văn phong, kết quả nhận được sẽ thiếu nhất quán
-Internationalizer thì khác. Đây là một **CLI pipeline** kết hợp dịch thuật LLM với:
+Internationalizer thì khác. Đây là một **CLI pipeline** kết hợp dịch thuật bằng LLM với:
-- **Bảng thuật ngữ theo ngôn ngữ** — đảm bảo tính nhất quán của thuật ngữ trên toàn bộ ứng dụng
-- **Hướng dẫn văn phong theo ngôn ngữ** — kiểm soát giọng văn, mức độ trang trọng, số nhiều và cách trình bày
-- **Bộ nhớ dịch thuật (Translation memory)** — bỏ qua các chuỗi không thay đổi, tiết kiệm chi phí gọi API
-- **Xác thực key** — phát hiện các bản dịch bị thiếu và lỗi khớp biến nội suy (interpolation) trước khi phát hành
+- **Bảng thuật ngữ theo từng ngôn ngữ** — thực thi thuật ngữ nhất quán trên toàn bộ ứng dụng của bạn
+- **Hướng dẫn văn phong theo từng ngôn ngữ** — kiểm soát giọng văn, mức độ trang trọng, dạng số nhiều và quy tắc kiểu chữ
+- **Bộ nhớ dịch thuật** — bỏ qua các chuỗi không thay đổi, tiết kiệm chi phí gọi API
+- **Xác thực tất định** — phát hiện các key bị thiếu hoặc thừa, sai lệch cấu trúc được bảo vệ, vấn đề thuật ngữ, cùng các lỗi số nhiều hoặc lỗi ICU trước khi phát hành
+
## Cài đặt
Cài đặt từ npm:
@@ -47,7 +48,7 @@ Hoặc chạy mà không cần cài đặt global:
npx internationalizer --help
```
-Gói npm sẽ cài đặt tệp nhị phân dựng sẵn tương ứng từ npm thông qua các dependency tùy chọn theo nền tảng.
+Gói npm sẽ cài đặt tệp nhị phân dựng sẵn phù hợp từ npm thông qua các dependency tùy chọn theo từng nền tảng.
Cài đặt bằng Go:
@@ -63,22 +64,28 @@ cd Internationalizer
go build -o internationalizer ./cmd/internationalizer
```
+
## Các gói npm
- Git tag và phiên bản gói npm phải khớp nhau, ví dụ `v0.1.0` và `0.1.0`
- Gói `internationalizer` gốc phụ thuộc vào các gói nền tảng như `internationalizer-darwin-arm64`
-- Các mục tiêu npm được hỗ trợ: macOS arm64/x64, Linux arm64/x64, Windows x64
+- Các target npm được hỗ trợ: macOS arm64/x64, Linux arm64/x64, Windows x64
- Việc xuất bản qua CI yêu cầu một GitHub secret có tên là `NPM_TOKEN`
+
## Bắt đầu nhanh
-1. Tạo tệp cấu hình trong thư mục gốc của dự án:
+1. Tạo một tệp cấu hình trong thư mục gốc của dự án:
```yaml
# .internationalizer.yml
source_locale: en
target_locales: [fr, de, es, ja]
-source_path: locales/en.json
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
llm:
provider: gemini
@@ -104,49 +111,78 @@ internationalizer translate --dry-run
internationalizer translate
```
-5. Xác thực tất cả các ngôn ngữ:
+5. Xác thực tất cả các locale:
```bash
internationalizer validate
```
+
## Lệnh
### `translate`
-Tìm các key bị thiếu và dịch chúng thông qua LLM.
+Tìm các key bị thiếu hoặc đã cũ và dịch chúng thông qua LLM.
```bash
internationalizer translate # translate all locales
internationalizer translate -l fr # translate French only
internationalizer translate --dry-run # preview without API calls
+internationalizer translate --adopt-existing # baseline existing translations without API calls
+internationalizer translate --refresh-policy # refresh prompt/style/model-stale entries
internationalizer translate --batch-size 20 # smaller batches
internationalizer translate --concurrency 2 # fewer parallel calls
```
+Trạng thái dịch báo cáo độc lập các điều kiện missing (bị thiếu), source-stale (nguồn cũ), policy-stale (chính sách cũ), current (hiện hành) và manually edited (chỉnh sửa thủ công), nhờ đó một chỉnh sửa thủ công không thể che giấu thay đổi từ nguồn hoặc chính sách. Các giá trị policy-stale được báo cáo nhưng chỉ được dịch lại khi có cờ `--refresh-policy`. Các giá trị chỉnh sửa thủ công không bao giờ bị ghi đè tự động. Hãy dùng `--adopt-existing` khi áp dụng manifest vào các bản dịch đã được duyệt hoặc khi chấp thuận rõ ràng một chỉnh sửa thủ công đã qua rà soát làm mốc cơ sở mới.
+
### `validate`
-Kiểm tra tất cả các tệp ngôn ngữ để tìm key bị thiếu, key thừa và lỗi khớp biến nội suy.
+Kiểm tra tất cả các tệp locale so với bundle nguồn tương ứng. Quá trình xác thực mặc định sẽ kiểm tra độ bao phủ cấu trúc (tỷ lệ phần trăm key đích bắt buộc hiện diện), báo cáo các key thừa dưới dạng cảnh báo, và báo lỗi nếu thiếu key, không khớp nội suy hoặc cấu trúc ICU MessageFormat không hợp lệ.
```bash
internationalizer validate # human-readable output
internationalizer validate --json # machine-readable JSON
internationalizer validate -q # exit code only
+internationalizer validate --strict # enforce translation quality rules
+internationalizer validate --require-state # require current manifest provenance
```
+`--strict` cũng báo cáo độ bao phủ bản dịch. Một giá trị ngôn ngữ trùng khớp với nguồn sẽ bị coi là chưa dịch trừ khi bảng thuật ngữ nêu rõ một mục từ nguồn và đích giống hệt nhau cho toàn bộ giá trị đó; `ignore_case` vẫn được áp dụng, nhưng một thuật ngữ trong bảng thuật ngữ nằm trong một giá trị dài hơn sẽ không được miễn trừ. Chế độ strict báo lỗi khi có key thừa, giá trị trùng khớp với nguồn, cấu trúc nội suy/HTML/mã/liên kết Markdown bị thay đổi, vi phạm bảng thuật ngữ và các dạng số nhiều đã cấu hình.
+
+`--require-state` xác minh từng target so với `.internationalizer.lock`. Lệnh sẽ báo lỗi khi một key chưa được theo dõi, hoặc khi mã băm nguồn, chính sách dịch hay target đã ghi nhận bị cũ. Cờ này có thể kết hợp với `--strict`.
+
+Báo cáo cho con người đọc và báo cáo JSON sử dụng các mã phát hiện ổn định:
+
+| Mã | Ý nghĩa |
+| --- | --- |
+| `missing_key` / `extra_key` | Tập hợp key nguồn và đích khác nhau |
+| `blank_translation` | Nguồn không rỗng nhưng target ở chế độ strict lại rỗng |
+| `source_identical` | Giá trị ngôn ngữ ở chế độ strict vẫn chưa được dịch |
+| `protected_structure_mismatch` | Cấu trúc nội suy, HTML, mã hoặc liên kết đã thay đổi |
+| `glossary_violation` | Không tìm thấy thuật ngữ đích hoặc biến thể được phê duyệt |
+| `plural_form_missing` | Thiếu một dạng số nhiều đã cấu hình của locale |
+| `icu_message_syntax` | Thông điệp ICU nguồn hoặc đích bị sai định dạng |
+| `icu_argument_mismatch` | Tên đối số, kiểu hoặc kiểu bộ định dạng của ICU khác nhau |
+| `icu_selector_mismatch` | Bộ chọn khác nhau hoặc danh mục số nhiều không hợp lệ cho locale đích |
+| `untracked` | Không có bản ghi manifest nào cho target |
+| `source_stale` | Nội dung nguồn đã thay đổi sau lần dịch được ghi nhận |
+| `policy_stale` | Prompt được tạo hoặc cài đặt mô hình đã thay đổi |
+| `target_modified` | Nội dung target khác với bản ghi manifest |
+
### `detect`
-Tự động phát hiện framework i18n và đề xuất cấu hình.
+Tự động phát hiện framework i18n và gợi ý cấu hình.
```bash
internationalizer detect
```
-Hỗ trợ: react-i18next, next-intl, vue-i18n, vanilla JSON, markdown docs.
+Hỗ trợ: react-i18next, next-intl, vue-i18n, vanilla JSON, tài liệu markdown.
### `glossary`
-Quản lý các thuật ngữ trong bảng thuật ngữ theo ngôn ngữ được áp dụng bắt buộc trong quá trình dịch.
+Quản lý các thuật ngữ trong bảng thuật ngữ theo từng ngôn ngữ được áp dụng bắt buộc trong quá trình dịch.
```bash
internationalizer glossary list --locale fr
@@ -164,6 +200,7 @@ internationalizer tm export # dump as JSON
internationalizer tm clear --force # delete all records
```
+
## Tham chiếu cấu hình
```yaml
@@ -173,10 +210,23 @@ internationalizer tm clear --force # delete all records
source_locale: en
# Languages to translate into (required)
-target_locales: [fr, de, es, ja, zh-CN, ar]
-
-# Path to the source locale file (required)
-source_path: locales/en.json
+target_locales: [fr, de, es, ja, yue, zh-CN, zh-TW, ar]
+
+# One or more source-to-target mappings (required).
+# {locale} is replaced with each configured target locale.
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
+ - id: docs
+ source: README.md
+ target: docs/i18n/{locale}.md
+ format: markdown
+
+# Backward compatibility: source_path still maps targets to sibling files
+# such as locales/fr.json. Prefer bundles for new projects.
+# source_path: locales/en.json
# LLM provider settings
llm:
@@ -185,7 +235,7 @@ llm:
# Model name defaults by provider:
# anthropic: claude-opus-5
- # openai: gpt-5.6-luna
+ # openai: gpt-5.6-luna (reasoning effort defaults to max)
# gemini: gemini-3.8-flash
# openrouter: deepseek/deepseek-v4-pro-0813
model: gemini-3.8-flash
@@ -196,6 +246,27 @@ llm:
# Base URL for OpenAI-compatible endpoints (optional)
# base_url: https://api.openai.com
+ # OpenAI GPT-5-series Responses API reasoning effort
+ # (default: max for the OpenAI provider)
+ reasoning_effort: max
+
+ # Optional LLM settings for individual target locales. An override using the
+ # global provider inherits unspecified global settings. A different provider
+ # uses that provider's defaults for unspecified settings.
+ locale_overrides:
+ yue:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-CN:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-TW:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+
# Keys per LLM call (default: 40)
batch_size: 40
@@ -210,11 +281,26 @@ glossary_dir: glossary
# Path to translation memory file (default: .internationalizer/tm.jsonl)
tm_path: .internationalizer/tm.jsonl
+
+# Versioned source, policy, target, and provenance state
+# (default: .internationalizer.lock; commit this file)
+manifest_path: .internationalizer.lock
+
+# Optional translation and strict-validation rules
+validation:
+ plural_style: i18next-v4 # generate and validate target-locale plural forms
```
+Mã định danh locale phải là các tag BCP 47 đúng chuẩn như `fr`, `pt-BR` hoặc `sr-Latn-RS`. Các locale đích tương đương chuẩn hóa (canonical-equivalent) sẽ bị từ chối do trùng lặp, và cấu hình ghi đè nhà cung cấp theo từng locale sẽ khớp theo cách viết tương đương chuẩn hóa. Trong ví dụ trên, các locale không có cấu hình ghi đè — bao gồm cả tiếng Nhật — sẽ kế thừa cấu hình Gemini toàn cục.
+
+Các giá trị ICU MessageFormat được phân tích cú pháp theo cấu trúc. Hỗ trợ các đối số đơn giản, `select`, `plural`, `selectordinal`, `number`, `date` và `time`, bao gồm cả thông điệp lồng nhau, độ dời số nhiều (plural offset), bộ chọn số chính xác và `#`. Quá trình xác thực kiểm tra cú pháp, kiểu đối số và kiểu bộ định dạng, độ dời số nhiều, sự đồng nhất nhánh select và các danh mục số nhiều CLDR của locale đích. Đầu ra từ nhà cung cấp phá vỡ các bất biến này sẽ bị từ chối trước khi tệp locale hoặc bản ghi bộ nhớ dịch thuật được ghi.
+
+Với `i18next-v4`, các họ số nhiều nguồn được nhận diện sẽ được mở rộng trong quá trình dịch thành các danh mục CLDR của locale đích. Danh mục chỉ có ở đích sẽ sử dụng giá trị `_other` của họ nguồn làm mẫu dịch thuật. Xác thực strict bắt buộc phải có các danh mục đích đó; các danh mục chỉ có ở nguồn là tùy chọn đối với các locale đích không sử dụng chúng.
+
+
## Hướng dẫn văn phong
-Hướng dẫn văn phong là các tệp Markdown được đưa vào prompt dịch của LLM. Chúng kiểm soát giọng văn, mức độ trang trọng, cách trình bày và các quy ước đặc thù khác của ngôn ngữ.
+Hướng dẫn văn phong là các tệp Markdown được đưa vào prompt dịch của LLM. Chúng kiểm soát giọng văn, mức độ trang trọng, quy tắc kiểu chữ và các quy ước đặc thù ngôn ngữ khác.
```
style-guides/
@@ -226,14 +312,17 @@ style-guides/
### Quy ước chung (`_conventions.md`)
-Định nghĩa các quy tắc áp dụng cho tất cả ngôn ngữ: cú pháp nội suy, bảo toàn HTML, quy ước loại chuỗi (nút bấm so với nhãn so với lỗi), v.v.
+Định nghĩa các quy tắc áp dụng cho mọi ngôn ngữ: cú pháp nội suy, bảo toàn HTML, quy ước loại chuỗi (nút bấm so với nhãn so với lỗi), v.v.
-### Hướng dẫn theo ngôn ngữ (`{locale}.md`)
+### Hướng dẫn theo từng ngôn ngữ (`{locale}.md`)
-Định nghĩa các quy tắc đặc thù của ngôn ngữ: mức độ trang trọng (tu so với vous), dấu câu (ngoặc kép, dấu hỏi ngược), các dạng số nhiều, định dạng ngày/số và bảng thuật ngữ.
+Định nghĩa các quy tắc theo từng ngôn ngữ: sắc thái trang trọng (tu so với vous), dấu câu (ngoặc kép, dấu hỏi ngược), các dạng số nhiều, định dạng ngày/số và bảng thuật ngữ.
-Xem [`examples/react-app/style-guides/`](examples/react-app/style-guides/) để biết ví dụ thực tế.
+Hướng dẫn văn phong là dữ liệu đầu vào chính sách bền vững chứ không phải đầu ra được tạo tự động. Internationalizer đọc nhưng không bao giờ ghi đè chúng. Nội dung của chúng được băm riêng biệt với bảng thuật ngữ và hợp đồng prompt, do đó thay đổi mã nguồn ứng dụng sẽ không làm bản dịch bị cũ. Việc chủ động chỉnh sửa hướng dẫn sẽ đánh dấu locale đó cần xem xét lại chính sách; thay đổi diễn đạt prompt nội bộ sẽ không gây ra điều này, trừ khi phiên bản hợp đồng prompt cũng thay đổi.
+Xem [`examples/react-app/style-guides/`](../../examples/react-app/style-guides/) để biết ví dụ thực tế.
+
+
## Định dạng bảng thuật ngữ
Các tệp bảng thuật ngữ là mảng JSON được lưu trữ trong `{glossary_dir}/{locale}.json`:
@@ -243,41 +332,49 @@ Các tệp bảng thuật ngữ là mảng JSON được lưu trữ trong `{glos
{
"source": "Dashboard",
"target": "Tableau de bord",
+ "variants": ["Panneau de contrôle"],
+ "enforcement": "error",
"ignore_case": false,
"whole_word": true
}
]
```
-Các thuật ngữ được đưa vào prompt của LLM dưới dạng bảng thuật ngữ, đảm bảo việc dịch nhất quán các thuật ngữ chính trên toàn bộ ứng dụng của bạn.
+`variants` liệt kê các dạng đích khác đã được duyệt. `enforcement` có thể là `error`, `warning` hoặc được bỏ qua để nhận hành vi mặc định là error. Các thuật ngữ được đưa vào prompt của LLM dưới dạng bảng thuật ngữ, đảm bảo tính nhất quán của bản dịch trên toàn bộ ứng dụng của bạn. Một mục từ chính xác như `{"source":"API","target":"API"}` cũng giúp miễn trừ toàn bộ giá trị trùng khớp với nguồn đó khỏi các phát hiện chưa dịch ở chế độ strict; mục từ này không miễn trừ một giá trị dài hơn chỉ đơn thuần chứa `API`.
+
## Bộ nhớ dịch thuật
-Bộ nhớ dịch thuật được lưu trữ dưới dạng tệp JSONL (mỗi dòng là một bản ghi JSON). Mỗi bản ghi chứa:
+Bộ nhớ dịch thuật được lưu trữ dưới dạng tệp JSONL (mỗi bản ghi JSON trên một dòng). Mỗi bản ghi bao gồm:
-- Key và giá trị nguồn
-- Giá trị đã dịch
-- Mã băm SHA-256 của giá trị nguồn
+- Bundle, key, giá trị nguồn, giá trị đã dịch và locale đích chuẩn hóa
+- Mã băm nguồn, hướng dẫn văn phong, bảng thuật ngữ, hợp đồng prompt và chính sách kết hợp
+- Nhà cung cấp và mô hình đã tạo bản dịch
- Dấu thời gian
-Trong các lần chạy tiếp theo, các chuỗi không thay đổi sẽ được lấy từ bộ nhớ cache TM mà không cần gọi LLM, giúp tiết kiệm cả thời gian và chi phí API. Tệp TM thân thiện với git và có thể được commit cùng với các tệp ngôn ngữ của bạn.
+Trong những lần chạy tiếp theo, các chuỗi có cùng mã băm nguồn và chính sách sẽ được phân phát từ bộ nhớ cache mà không cần gọi LLM. Đường dẫn mặc định nằm trong thư mục bị bỏ qua `.internationalizer/`, do đó nó hoạt động như một bộ nhớ cache cục bộ. Hãy đặt `tm_path` tới một vị trí được theo dõi nếu dự án của bạn chủ đích chia sẻ bộ nhớ dịch thuật. Tệp manifest `.internationalizer.lock` có thể rà soát được quản lý phiên bản riêng biệt.
+
## Các định dạng được hỗ trợ
| Định dạng | Phần mở rộng | Chế độ |
|--------|-----------|------|
-| JSON | `.json` | Key-value (lồng nhau, làm phẳng bằng dấu chấm) |
+| JSON | `.json` | Key-value (lồng nhau, làm phẳng theo ký hiệu dấu chấm) |
| YAML | `.yml`, `.yaml` | Key-value (bảo toàn chú thích và thứ tự) |
-| Markdown | `.md`, `.mdx` | Dịch toàn bộ tài liệu |
+| Markdown | `.md`, `.mdx` | Phần mở đầu (preamble) và các phần cấp H2 |
+
+Các target Markdown chứa chú thích `internationalizer:unit` ẩn trước các phần H2. Những đánh dấu ổn định này cho phép Internationalizer thêm, di chuyển hoặc chỉnh sửa một phần nguồn mà không cần dịch lại các phần không liên quan khác. Các tài liệu hiện có chưa được đánh dấu sẽ nhận các điểm đánh dấu này trong lần cập nhật thành công tiếp theo.
+
## Phát hiện loại dự án
`internationalizer detect` xác định thiết lập i18n của bạn bằng cách kiểm tra:
-- Các dependency trong `package.json` cho react-i18next, next-intl hoặc vue-i18n
-- Cấu trúc thư mục khớp với các mẫu ngôn ngữ phổ biến
+- Các dependency trong `package.json` để tìm react-i18next, next-intl hoặc vue-i18n
+- Cấu trúc thư mục khớp với các khuôn mẫu locale phổ biến
- Phần mở rộng tệp và quy ước đặt tên
+
## Kiến trúc
```
@@ -292,18 +389,23 @@ internal/
openai.go OpenAI / compatible backend
gemini.go Google Gemini via AI Studio backend
OpenRouter uses openai.go with custom base_url
+ locale/ BCP 47 identity and CLDR plural categories
+ message/ ICU MessageFormat parser and structural comparison
+ policy/ Stable translation-policy hashing
+ state/ Versioned translation manifest
styleguide/ Style guide loader
tm/ JSONL translation memory
translate/ Translation orchestrator
validate/ Locale validation and diffing
```
+
## So sánh với các giải pháp thay thế
| Tính năng | Internationalizer | i18next | Crowdin | LLM thông thường |
|---------|------------------|---------|---------|-------------|
| Dịch thuật bằng LLM | Có | Không | Một phần | Có |
-| Hướng dẫn văn phong theo ngôn ngữ | Có | Không | Không | Không |
+| Hướng dẫn văn phong theo từng ngôn ngữ | Có | Không | Không | Không |
| Áp dụng bảng thuật ngữ | Có | Không | Có | Không |
| Bộ nhớ dịch thuật | Có | Không | Có | Không |
| CLI / thực thi cục bộ | Có | N/A | Không | Thủ công |
@@ -311,11 +413,14 @@ internal/
| Không phụ thuộc SaaS | Có | Có | Không | Tùy trường hợp |
| Mã nguồn mở (AGPL-3.0) | Có | Có | Không | Tùy trường hợp |
+
## Giấy phép
-[AGPL-3.0](LICENSE)
+[AGPL-3.0](../../LICENSE)
-## Đóng góp
+Xem [THIRD_PARTY_NOTICES.md](../../THIRD_PARTY_NOTICES.md) để biết thông báo về dependency.
-Xem [CONTRIBUTING.md](CONTRIBUTING.md) để biết hướng dẫn và thiết lập phát triển. Tất cả các đóng góp đều yêu cầu xác nhận DCO.
+
+## Đóng góp
+Xem [CONTRIBUTING.md](../../CONTRIBUTING.md) để biết hướng dẫn và thiết lập phát triển. Tất cả các đóng góp đều yêu cầu ký xác nhận DCO.
diff --git a/docs/i18n/yue.md b/docs/i18n/yue.md
index 8566bb1..85d9fe4 100644
--- a/docs/i18n/yue.md
+++ b/docs/i18n/yue.md
@@ -9,23 +9,31 @@
專為軟件項目而設嘅 AI 原生國際化 pipeline。使用 LLM 翻譯、驗證同管理 i18n 檔案。
[](https://github.com/Tom-R-Main/Internationalizer/actions/workflows/ci.yml)
-[](LICENSE)
+[](../../LICENSE)
-## 點解要用 Internationalizer?
+
+العربية · বাংলা · Čeština · Dansk · Deutsch · Ελληνικά · Español · Suomi · Français · עברית · हिन्दी · Indonesia · Italiano · 日本語 · 한국어 · Bahasa Melayu
Nederlands · ਪੰਜਾਬੀ · Polski · Português · Română · Русский · Svenska · తెలుగు · ไทย · Türkçe · Українська · Tiếng Việt · 粵語 · 简体中文 · 繁體中文
+
+
+---
-大部分 i18n 工具一係 runtime 庫(i18next、react-intl),一係 key 管理 SaaS 平台(Crowdin、Lokalise)。佢哋都冇辦法好好解決實際嘅翻譯問題:
+
+## 點解要揀 Internationalizer?
-- **人手翻譯** 喺語言數量多嗰陣好難擴展
+大部分 i18n 工具一係 runtime 函式庫(i18next、react-intl),一係 key 管理 SaaS 平台(Crowdin、Lokalise)。但佢哋都無法好好解決實際嘅翻譯問題:
+
+- **人手翻譯** 喺語言數量多咗嗰陣根本應付唔嚟
- **機器翻譯 API**(Google Translate、DeepL)會忽略你嘅術語、語氣同 UI 慣例
-- **通用 LLM 翻譯** 效果比較好,但如果冇詞彙表同風格指南,結果會唔一致
+- **通用 LLM 翻譯** 雖然好啲,但如果冇詞彙表同風格指南,翻譯出嚟嘅結果就會好唔一致
-Internationalizer 唔同。佢係一個結合咗 LLM 翻譯嘅 **CLI pipeline**,並提供:
+Internationalizer 截然不同。佢係一個結合 LLM 翻譯同以下功能嘅 **CLI pipeline**:
-- **每種語言嘅詞彙表** — 確保成個應用程式嘅術語一致
-- **每種語言嘅風格指南** — 控制語氣、正式程度、眾數同排版
-- **翻譯記憶庫 (Translation memory)** — 跳過冇改過嘅字串,慳返 API 費用
-- **Key 驗證** — 喺發佈之前搵出漏譯同插值 (interpolation) 唔匹配嘅問題
+- **各語言專屬詞彙表** — 喺成個應用程式中強制維持一致嘅術語
+- **各語言專屬風格指南** — 控制語氣、正式程度、眾數同排版規則
+- **翻譯記憶庫** — 跳過冇修改過嘅字串,慳返 API 呼叫費用
+- **確定性驗證** — 喺發佈前即時攔截缺少或多餘嘅 key、受保護結構跑位、詞彙表違規,以及眾數或 ICU 錯誤
+
## 安裝
透過 npm 安裝:
@@ -34,13 +42,13 @@ Internationalizer 唔同。佢係一個結合咗 LLM 翻譯嘅 **CLI pipeline**
npm install -g internationalizer
```
-或者唔需要全域安裝直接執行:
+或者唔使全域安裝直接執行:
```bash
npx internationalizer --help
```
-npm 套件會透過特定平台嘅 optional dependencies 從 npm 安裝相應嘅預先編譯二進制檔案。
+npm 套件會透過特定平台嘅 optional dependencies,從 npm 安裝對應嘅預先編譯二進制檔案。
透過 Go 安裝:
@@ -48,7 +56,7 @@ npm 套件會透過特定平台嘅 optional dependencies 從 npm 安裝相應嘅
go install github.com/Tom-R-Main/Internationalizer/cmd/internationalizer@latest
```
-或者從源碼編譯:
+或者從原始碼編譯:
```bash
git clone https://github.com/Tom-R-Main/Internationalizer.git
@@ -56,22 +64,28 @@ cd Internationalizer
go build -o internationalizer ./cmd/internationalizer
```
+
## npm 套件
- Git tag 同 npm 套件版本必須一致,例如 `v0.1.0` 同 `0.1.0`
-- 根目錄嘅 `internationalizer` 套件依賴於平台套件,例如 `internationalizer-darwin-arm64`
+- 根目錄嘅 `internationalizer` 套件依賴特定平台套件,例如 `internationalizer-darwin-arm64`
- 支援嘅 npm 目標平台:macOS arm64/x64、Linux arm64/x64、Windows x64
-- CI 發佈需要一個名為 `NPM_TOKEN` 嘅 GitHub secret
+- CI 發佈流程需要一個名為 `NPM_TOKEN` 嘅 GitHub secret
-## 快速開始
+
+## 快速上手
-1. 喺你嘅項目根目錄建立一個設定檔:
+1. 喺你嘅項目根目錄建立設定檔:
```yaml
# .internationalizer.yml
source_locale: en
target_locales: [fr, de, es, ja]
-source_path: locales/en.json
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
llm:
provider: gemini
@@ -97,36 +111,65 @@ internationalizer translate --dry-run
internationalizer translate
```
-5. 驗證所有語言:
+5. 驗證所有語言地區檔案:
```bash
internationalizer validate
```
+
## 指令
### `translate`
-搵出遺漏嘅 key 並透過 LLM 進行翻譯。
+搵出缺少或者過期嘅 key,並透過 LLM 進行翻譯。
```bash
-internationalizer translate # 翻譯所有語言
-internationalizer translate -l fr # 只翻譯法文
-internationalizer translate --dry-run # 預覽,唔呼叫 API
-internationalizer translate --batch-size 20 # 較細嘅 batch
-internationalizer translate --concurrency 2 # 減少並行呼叫
+internationalizer translate # translate all locales
+internationalizer translate -l fr # translate French only
+internationalizer translate --dry-run # preview without API calls
+internationalizer translate --adopt-existing # baseline existing translations without API calls
+internationalizer translate --refresh-policy # refresh prompt/style/model-stale entries
+internationalizer translate --batch-size 20 # smaller batches
+internationalizer translate --concurrency 2 # fewer parallel calls
```
+翻譯狀態會獨立報告缺少、來源過期、策略過期、最新同手動修改過嘅狀況,所以手動編輯唔會遮蔽來源變更或策略更新。策略過期嘅項目會被報告出嚟,但只有加咗 `--refresh-policy` 嗰陣先會重新翻譯。手動修改過嘅值永遠唔會被自動覆蓋。當第一次為已經過審查嘅翻譯引入 manifest,或者要明確將審查過嘅手動編輯採納為新嘅基準嗰陣,請使用 `--adopt-existing`。
+
### `validate`
-檢查所有語言檔案有冇遺漏嘅 key、多出嘅 key 同插值唔匹配嘅問題。
+對照來源 bundle 檢查所有語言地區檔案。預設驗證會檢查結構覆蓋率(所需目標 key 存在嘅百分比),將多出嘅 key 列為警告,並喺發現缺少 key、插值不匹配或無效嘅 ICU MessageFormat 結構時報錯。
```bash
-internationalizer validate # 人類可讀嘅輸出
-internationalizer validate --json # 機器可讀嘅 JSON
-internationalizer validate -q # 只回傳 exit code
+internationalizer validate # human-readable output
+internationalizer validate --json # machine-readable JSON
+internationalizer validate -q # exit code only
+internationalizer validate --strict # enforce translation quality rules
+internationalizer validate --require-state # require current manifest provenance
```
+`--strict` 亦會報告已翻譯覆蓋率。除咗詞彙表明確包含完全相同來源、相同目標嘅整句完整詞條之外,同來源完全一樣嘅語言文字會被視為未翻譯;雖然系統支援 `ignore_case`,但只係喺較長文字入面嵌入詞彙表詞條並唔能夠獲得豁免。嚴格模式會喺出現多餘 key、與來源完全一致嘅值、插值/HTML/代碼/Markdown 連結結構變更、詞彙表違規同設定嘅眾數形式缺少時報錯。
+
+`--require-state` 會對照 `.internationalizer.lock` 驗證每個目標。當 key 未被追蹤,或者記錄嘅來源、翻譯策略或目標 hash 過期嗰陣,驗證就會失敗。呢個參數可以同 `--strict` 一齊用。
+
+人類可讀報告同 JSON 報告都使用穩定嘅問題代碼:
+
+| 代碼 | 意思 |
+| --- | --- |
+| `missing_key` / `extra_key` | 來源同目標嘅 key 組合不一致 |
+| `blank_translation` | 非空白嘅來源喺嚴格模式下對應咗空白嘅目標 |
+| `source_identical` | 嚴格模式下語言文字依然保持未翻譯狀態 |
+| `protected_structure_mismatch` | 插值、HTML、代碼或連結結構有所改變 |
+| `glossary_violation` | 搵唔到認可嘅目標詞條或變體 |
+| `plural_form_missing` | 設定嘅語言地區眾數形式缺少咗 |
+| `icu_message_syntax` | 來源或目標嘅 ICU 訊息格式有誤 |
+| `icu_argument_mismatch` | ICU 引數名稱、類型或格式化樣式不一致 |
+| `icu_selector_mismatch` | 選擇器不一致,或者眾數分類對該目標語言地區無效 |
+| `untracked` | 該目標喺 manifest 中完全冇記錄 |
+| `source_stale` | 來源內容喺記錄嘅翻譯之後發生咗變更 |
+| `policy_stale` | 產生嘅 prompt 或模型設定發生咗變更 |
+| `target_modified` | 目標內容同 manifest 記錄唔同 |
+
### `detect`
自動偵測 i18n 框架並建議設定。
@@ -135,11 +178,11 @@ internationalizer validate -q # 只回傳 exit code
internationalizer detect
```
-支援:react-i18next、next-intl、vue-i18n、原生 JSON、markdown 文件。
+支援:react-i18next、next-intl、vue-i18n、一般 JSON、Markdown 文件。
### `glossary`
-管理每種語言嘅詞彙表,喺翻譯期間強制執行。
+管理翻譯過程中強制執行嘅各語言詞彙表詞條。
```bash
internationalizer glossary list --locale fr
@@ -149,84 +192,137 @@ internationalizer glossary remove --locale fr --source "Dashboard"
### `tm`
-管理翻譯記憶庫(之前翻譯過嘅字串嘅 JSONL 快取)。
+管理翻譯記憶庫(儲存之前翻譯字串嘅 JSONL 快取)。
```bash
-internationalizer tm stats # 顯示記錄數量
-internationalizer tm export # 匯出為 JSON
-internationalizer tm clear --force # 刪除所有記錄
+internationalizer tm stats # show record counts
+internationalizer tm export # dump as JSON
+internationalizer tm clear --force # delete all records
```
+
## 設定參考
```yaml
# .internationalizer.yml
-# 來源語言(預設:en)
+# Source language (default: en)
source_locale: en
-# 要翻譯成嘅語言(必填)
-target_locales: [fr, de, es, ja, zh-CN, ar]
-
-# 來源語言檔案嘅路徑(必填)
-source_path: locales/en.json
-
-# LLM 供應商設定
+# Languages to translate into (required)
+target_locales: [fr, de, es, ja, yue, zh-CN, zh-TW, ar]
+
+# One or more source-to-target mappings (required).
+# {locale} is replaced with each configured target locale.
+bundles:
+ - id: app
+ source: locales/en.json
+ target: locales/{locale}.json
+ format: json
+ - id: docs
+ source: README.md
+ target: docs/i18n/{locale}.md
+ format: markdown
+
+# Backward compatibility: source_path still maps targets to sibling files
+# such as locales/fr.json. Prefer bundles for new projects.
+# source_path: locales/en.json
+
+# LLM provider settings
llm:
- # 供應商:"anthropic"、"openai"、"gemini" 或 "openrouter"(預設:gemini)
+ # Provider: "anthropic", "openai", "gemini", or "openrouter" (default: gemini)
provider: gemini
- # 預設模型名稱(按供應商):
+ # Model name defaults by provider:
# anthropic: claude-opus-5
- # openai: gpt-5.6-luna
+ # openai: gpt-5.6-luna (reasoning effort defaults to max)
# gemini: gemini-3.8-flash
# openrouter: deepseek/deepseek-v4-pro-0813
model: gemini-3.8-flash
- # 包含 API key 嘅環境變數
+ # Environment variable containing the API key
api_key_env: GOOGLE_AI_STUDIO_API_KEY
- # 兼容 OpenAI 嘅 endpoint 嘅 Base URL(選填)
+ # Base URL for OpenAI-compatible endpoints (optional)
# base_url: https://api.openai.com
-# 每次 LLM 呼叫嘅 key 數量(預設:40)
+ # OpenAI GPT-5-series Responses API reasoning effort
+ # (default: max for the OpenAI provider)
+ reasoning_effort: max
+
+ # Optional LLM settings for individual target locales. An override using the
+ # global provider inherits unspecified global settings. A different provider
+ # uses that provider's defaults for unspecified settings.
+ locale_overrides:
+ yue:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-CN:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+ zh-TW:
+ provider: openrouter
+ model: deepseek/deepseek-v4-flash-0731
+ api_key_env: OPENROUTER_API_KEY
+
+# Keys per LLM call (default: 40)
batch_size: 40
-# 並行 LLM 呼叫數量(預設:4)
+# Parallel LLM calls (default: 4)
concurrency: 4
-# 包含每種語言風格指南 Markdown 檔案嘅目錄(預設:style-guides)
+# Directory containing per-locale style guide Markdown files (default: style-guides)
style_guides_dir: style-guides
-# 包含每種語言詞彙表 JSON 檔案嘅目錄(預設:glossary)
+# Directory containing per-locale glossary JSON files (default: glossary)
glossary_dir: glossary
-# 翻譯記憶庫檔案嘅路徑(預設:.internationalizer/tm.jsonl)
+# Path to translation memory file (default: .internationalizer/tm.jsonl)
tm_path: .internationalizer/tm.jsonl
+
+# Versioned source, policy, target, and provenance state
+# (default: .internationalizer.lock; commit this file)
+manifest_path: .internationalizer.lock
+
+# Optional translation and strict-validation rules
+validation:
+ plural_style: i18next-v4 # generate and validate target-locale plural forms
```
+語言地區識別碼必須係格式正確嘅 BCP 47 標籤,例如 `fr`、`pt-BR` 或 `sr-Latn-RS`。規範等價嘅目標語言地區會被當成重複項而拒絕,而特定語言地區嘅供應商覆寫亦會以規範等價嘅寫法進行比對。喺上面嘅範例入面,冇特別設定覆寫嘅語言地區(包括日文)會直接繼承全域嘅 Gemini 設定。
+
+系統會對 ICU MessageFormat 嘅值進行結構化解析。支援簡單引數、`select`、`plural`、`selectordinal`、`number`、`date` 同 `time`,包含巢狀訊息、眾數偏移(plural offsets)、精確數字選擇器同 `#`。驗證過程會檢查語法、引數類型同格式化樣式、眾數偏移、select 分支一致性,以及目標語言地區嘅 CLDR 眾數分類。只要供應商輸出破壞咗呢啲不變量,喺寫入語言地區檔案或翻譯記憶庫記錄之前就會被拒絕。
+
+配合 `i18next-v4`,已識別嘅來源眾數家族喺翻譯期間會展開為目標語言地區嘅 CLDR 分類。目標特有嘅分類會使用來源家族嘅 `_other` 數值作為翻譯範本。嚴格驗證要求必須存在呢啲目標分類;至於來源特有嘅分類,對於唔使用佢哋嘅目標語言地區嚟講係選填嘅。
+
+
## 風格指南
-風格指南係 Markdown 檔案,會被注入到 LLM 翻譯 prompt 入面。佢哋控制語氣、正式程度、排版同其他特定語言嘅慣例。
+風格指南係 Markdown 檔案,會被注入到 LLM 翻譯 prompt 入面。佢哋負責控制語氣、正式程度、排版以及其他特定語言嘅慣例。
```
style-guides/
- _conventions.md # 所有語言共用嘅規則
- fr.md # 法文專用規則
- ja.md # 日文專用規則
- ar.md # 阿拉伯文專用規則
+ _conventions.md # shared rules for all languages
+ fr.md # French-specific rules
+ ja.md # Japanese-specific rules
+ ar.md # Arabic-specific rules
```
### 共用慣例 (`_conventions.md`)
-定義適用於所有語言嘅規則:插值語法、保留 HTML、字串類型慣例(按鈕 vs 標籤 vs 錯誤)等。
+定義適用於所有語言嘅規則:插值語法、保留 HTML、字串類型慣例(按鈕 vs 標籤 vs 錯誤訊息)等。
-### 每種語言嘅指南 (`{locale}.md`)
+### 各語言專屬指南 (`{locale}.md`)
-定義特定語言嘅規則:正式程度(tu vs vous)、標點符號(書名號、倒問號)、眾數形式、日期/數字格式,以及術語詞彙表。
+定義特定語言嘅規則:敬語程度(tu vs. vous)、標點符號(法文引號、倒問號)、眾數形式、日期/數字格式同術語詞彙表。
-請參考 [`examples/react-app/style-guides/`](examples/react-app/style-guides/) 嘅實際例子。
+風格指南係持久嘅策略輸入,而唔係產生出嚟嘅輸出。Internationalizer 只會讀取佢哋,永遠唔會重寫。佢哋嘅內容 hash 係獨立於詞彙表同 prompt 協定之外分開計算嘅,所以應用程式代碼變更唔會導致翻譯過期。修改風格指南會特登將嗰個語言地區標記為需要策略審查;修改內部 prompt 字眼則唔會,除非 prompt 協定版本同時改變。
+完整運作範例請參考 [`examples/react-app/style-guides/`](../../examples/react-app/style-guides/)。
+
+
## 詞彙表格式
詞彙表檔案係儲存喺 `{glossary_dir}/{locale}.json` 嘅 JSON 陣列:
@@ -236,79 +332,95 @@ style-guides/
{
"source": "Dashboard",
"target": "Tableau de bord",
+ "variants": ["Panneau de contrôle"],
+ "enforcement": "error",
"ignore_case": false,
"whole_word": true
}
]
```
-術語會作為術語表注入到 LLM prompt 入面,確保成個應用程式嘅關鍵術語翻譯一致。
+`variants` 列出其他認可嘅目標形式。`enforcement` 可以係 `error`、`warning`,或者省略以使用預設嘅 error 行為。詞條會作為術語表注入到 LLM prompt 入面,確保應用程式嘅翻譯一致。好似 `{"source":"API","target":"API"}` 咁樣嘅完全相符項目,亦可以豁免該完整與來源相同嘅值喺嚴格模式下被列為未翻譯問題;但如果只係較長文字入面咁啱包含 `API`,就唔會獲得豁免。
+
## 翻譯記憶庫
翻譯記憶庫儲存為 JSONL 檔案(每行一筆 JSON 記錄)。每筆記錄包含:
-- 來源 key 同 value
-- 翻譯後嘅 value
-- 來源 value 嘅 SHA-256 hash
+- bundle、key、來源值、翻譯值同規範目標語言地區
+- 來源、風格指南、詞彙表、prompt 協定同合併策略 hash
+- 產生該翻譯嘅供應商同模型
- 時間戳記
-喺之後嘅執行中,冇改過嘅字串會從 TM 快取中提供,而唔需要呼叫 LLM,節省時間同 API 費用。TM 檔案對 git 友好,可以同你嘅語言檔案一齊 commit。
+喺後續嘅執行中,只要來源同策略 hash 相同嘅字串,就可以直接從快取讀取,唔需要呼叫 LLM。預設路徑位於被忽略嘅 `.internationalizer/` 目錄入面,所以佢只係一個本機快取。如果你嘅項目打算共享翻譯記憶庫,請將 `tm_path` 設定為納入版本控制嘅位置。可供審查嘅 `.internationalizer.lock` manifest 則係分開進行版本控制嘅。
+
## 支援嘅格式
| 格式 | 副檔名 | 模式 |
-|--------|-----------|------|
-| JSON | `.json` | Key-value(巢狀,點號表示法攤平) |
+| --- | --- | --- |
+| JSON | `.json` | Key-value(巢狀,點標記法扁平化) |
| YAML | `.yml`, `.yaml` | Key-value(保留註解同順序) |
-| Markdown | `.md`, `.mdx` | 成份文件翻譯 |
+| Markdown | `.md`, `.mdx` | 前言(preamble)同 H2 級段落 |
+
+Markdown 目標檔案喺 H2 段落前會包含睇唔見嘅 `internationalizer:unit` 註解。呢啲穩定標記令 Internationalizer 可以新增、移動或編輯單一來源段落,而唔需要重新翻譯其他無關嘅段落。現有未加入標記嘅文件會喺下一次成功更新嗰陣自動補上標記。
+
## 項目類型偵測
-`internationalizer detect` 透過檢查以下項目嚟識別你嘅 i18n 設定:
+`internationalizer detect` 會透過檢查以下項目嚟識別你嘅 i18n 設定:
-- `package.json` 裡面有冇 react-i18next、next-intl 或 vue-i18n 嘅 dependencies
-- 符合常見語言模式嘅目錄結構
-- 副檔名同命名慣例
+- `package.json` 裡面嘅 react-i18next、next-intl 或 vue-i18n 依賴套件
+- 符合常見語言地區模式嘅目錄結構
+- 檔案副檔名同命名慣例
+
## 架構
```
-cmd/internationalizer/ CLI 進入點同指令定義
+cmd/internationalizer/ CLI entry point and command definitions
internal/
- config/ 載入 YAML 設定同預設值
- detect/ 自動偵測項目類型
- formats/ 格式解析器(JSON、YAML、Markdown)
- glossary/ 管理每種語言嘅詞彙表
- llm/ LLM 供應商介面 + 實作
- anthropic.go Anthropic Claude 後端
- openai.go OpenAI / 兼容後端
- gemini.go 透過 AI Studio 嘅 Google Gemini 後端
- OpenRouter 使用 openai.go 配合自訂 base_url
- styleguide/ 風格指南載入器
- tm/ JSONL 翻譯記憶庫
- translate/ 翻譯協調器
- validate/ 語言驗證同差異比對
+ config/ YAML config loading with defaults
+ detect/ Project type auto-detection
+ formats/ Format parsers (JSON, YAML, Markdown)
+ glossary/ Per-locale glossary management
+ llm/ LLM provider interface + implementations
+ anthropic.go Anthropic Claude backend
+ openai.go OpenAI / compatible backend
+ gemini.go Google Gemini via AI Studio backend
+ OpenRouter uses openai.go with custom base_url
+ locale/ BCP 47 identity and CLDR plural categories
+ message/ ICU MessageFormat parser and structural comparison
+ policy/ Stable translation-policy hashing
+ state/ Versioned translation manifest
+ styleguide/ Style guide loader
+ tm/ JSONL translation memory
+ translate/ Translation orchestrator
+ validate/ Locale validation and diffing
```
+
## 替代方案比較
| 功能 | Internationalizer | i18next | Crowdin | 通用 LLM |
-|---------|------------------|---------|---------|-------------|
+| --- | --- | --- | --- | --- |
| LLM 驅動翻譯 | 是 | 否 | 部分 | 是 |
-| 每種語言風格指南 | 是 | 否 | 否 | 否 |
+| 各語言專屬風格指南 | 是 | 否 | 否 | 否 |
| 強制執行詞彙表 | 是 | 否 | 是 | 否 |
| 翻譯記憶庫 | 是 | 否 | 是 | 否 |
-| CLI / 本機執行 | 是 | 不適用 | 否 | 手動 |
+| CLI / 本地執行 | 是 | 不適用 | 否 | 手動 |
| Git 友好檔案 | 是 | 是 | 部分 | 手動 |
-| 冇 SaaS 依賴 | 是 | 是 | 否 | 視情況而定 |
+| 無 SaaS 依賴 | 是 | 是 | 否 | 視情況而定 |
| 開源 (AGPL-3.0) | 是 | 是 | 否 | 視情況而定 |
+
## 授權條款
-[AGPL-3.0](LICENSE)
+[AGPL-3.0](../../LICENSE)
-## 貢獻
+請參閱 [THIRD_PARTY_NOTICES.md](../../THIRD_PARTY_NOTICES.md) 查看依賴套件聲明。
-請參閱 [CONTRIBUTING.md](CONTRIBUTING.md) 了解開發設定同指南。所有貢獻都需要 DCO 簽署。
+
+## 參與貢獻
+請參閱 [CONTRIBUTING.md](../../CONTRIBUTING.md) 了解開發環境設定同指引。所有貢獻都需要符合 DCO 簽署要求。
diff --git a/docs/i18n/zh-CN.md b/docs/i18n/zh-CN.md
index f96d4cc..40e61ec 100644
--- a/docs/i18n/zh-CN.md
+++ b/docs/i18n/zh-CN.md
@@ -6,7 +6,7 @@
# Internationalizer
-面向软件项目的 AI 原生国际化流水线。基于大语言模型(LLM)实现 i18n 文件的翻译、校验与管理。
+面向软件项目的 AI 原生国际化流水线。基于 LLM 翻译、校验和管理 i18n 文件。
[](https://github.com/Tom-R-Main/Internationalizer/actions/workflows/ci.yml)
[](../../LICENSE)
@@ -16,22 +16,22 @@
---
-
+
## 为什么选择 Internationalizer?
-多数 i18n 工具要么属于运行时库(如 i18next、react-intl),要么属于翻译键管理 SaaS 平台(如 Crowdin、Lokalise)。它们均未能从根本上解决实际翻译问题:
-
-- **人工翻译**:支持语言一旦增多便难以扩展
-- **机器翻译 API**(Google Translate、DeepL):无视术语库、语气要求与 UI 规范
-- **通用 LLM 翻译**:效果虽有提升,但缺少术语表与样式指南时,译文风格容易脱节
+多数 i18n 工具要么是运行时库(i18next、react-intl),要么是键管理 SaaS 平台(Crowdin、Lokalise)。它们都未能真正解决实际的翻译问题:
-Internationalizer 则另辟蹊径。它是一套 **CLI 流水线**,将 LLM 翻译与以下能力深度结合:
+- **人工翻译**在超出几种语言后便难以扩展
+- **机器翻译 API**(Google Translate、DeepL)会忽略术语表、语气和 UI 规范
+- **通用 LLM 翻译**效果更好,但缺少术语表与风格指南会导致译文风格不一致
-- **各语言术语表** — 确保全系统专业术语统一
-- **各语言风格指南** — 规范语气、正式程度、复数规则及排版格式
-- **翻译记忆库** — 自动跳过未修改字段,节省 API 调用开销
-- **确定性校验** — 在发布前发现翻译键缺失或多余、受保护结构变更、术语表违规,以及复数或 ICU 错误
+Internationalizer 与众不同。它是一套 **CLI 流水线**,将 LLM 翻译与以下能力深度结合:
+- **各语言专属术语表** — 在全应用范围内统一专业术语
+- **各语言专属风格指南** — 把控语气、正式度、复数规则与排版格式
+- **翻译记忆库** — 跳过未修改的字符串,节省 API 调用成本
+- **确定性校验** — 在代码发布前拦截缺失或多余的键、受保护结构偏离、术语表问题,以及复数或 ICU 错误
+
## 安装
通过 npm 安装:
@@ -40,13 +40,13 @@ Internationalizer 则另辟蹊径。它是一套 **CLI 流水线**,将 LLM 翻
npm install -g internationalizer
```
-无需全局安装直接运行:
+或无需全局安装直接运行:
```bash
npx internationalizer --help
```
-npm 包会借助对应平台的可选依赖,从 npm 拉取并安装匹配的预构建二进制文件。
+npm 包会通过特定平台的可选依赖项从 npm 安装匹配的预构建二进制文件。
使用 Go 安装:
@@ -54,24 +54,24 @@ npm 包会借助对应平台的可选依赖,从 npm 拉取并安装匹配的
go install github.com/Tom-R-Main/Internationalizer/cmd/internationalizer@latest
```
-从源码编译:
+或从源码构建:
```bash
git clone https://github.com/Tom-R-Main/Internationalizer.git
cd Internationalizer
go build -o internationalizer ./cmd/internationalizer
```
+
+## npm 包
-## npm 软件包
-
-- Git 标签版本必须与 npm 包版本严格一致,例如 `v0.1.0` 与 `0.1.0`
-- 顶层 `internationalizer` 包依赖各平台分发包,例如 `internationalizer-darwin-arm64`
+- Git 标签与 npm 包版本必须一致,例如 `v0.1.0` 与 `0.1.0`
+- 根包 `internationalizer` 依赖各平台分发包,例如 `internationalizer-darwin-arm64`
- 支持的 npm 目标平台:macOS arm64/x64、Linux arm64/x64、Windows x64
-- CI 发布流水线需在 GitHub Secret 中配置 `NPM_TOKEN`
-
-## 快速上手
+- CI 发布需要配置名为 `NPM_TOKEN` 的 GitHub Secret
+
+## 快速开始
-1. 在项目根目录下创建配置文件:
+1. 在项目根目录创建配置文件:
```yaml
# .internationalizer.yml
@@ -89,7 +89,7 @@ llm:
api_key_env: GOOGLE_AI_STUDIO_API_KEY
```
-2. 配置 API 密钥:
+2. 设置 API 密钥:
```bash
export GOOGLE_AI_STUDIO_API_KEY=your-ai-studio-key
@@ -101,7 +101,7 @@ export GOOGLE_AI_STUDIO_API_KEY=your-ai-studio-key
internationalizer translate --dry-run
```
-4. 执行翻译:
+4. 运行翻译:
```bash
internationalizer translate
@@ -112,72 +112,72 @@ internationalizer translate
```bash
internationalizer validate
```
-
-## 命令列表
+
+## 命令
### `translate`
-检索缺失或已过期的键,并通过 LLM 自动完成翻译。
+查找缺失或过期的键,并通过 LLM 进行翻译。
```bash
-internationalizer translate # 翻译全部语言环境
-internationalizer translate -l fr # 仅翻译法语
-internationalizer translate --dry-run # 预览变更,不产生 API 调用
-internationalizer translate --adopt-existing # 将现有翻译纳为基线,不产生 API 调用
-internationalizer translate --refresh-policy # 重新翻译因提示词/样式指南/模型变更而过期的条目
-internationalizer translate --batch-size 20 # 减小批处理大小
-internationalizer translate --concurrency 2 # 降低并发请求数
+internationalizer translate # translate all locales
+internationalizer translate -l fr # translate French only
+internationalizer translate --dry-run # preview without API calls
+internationalizer translate --adopt-existing # baseline existing translations without API calls
+internationalizer translate --refresh-policy # refresh prompt/style/model-stale entries
+internationalizer translate --batch-size 20 # smaller batches
+internationalizer translate --concurrency 2 # fewer parallel calls
```
-翻译状态会分别独立跟踪缺失(missing)、源文过期(source-stale)、策略过期(policy-stale)、最新(current)以及手动修改(manually edited)等情况,手动编辑不会掩盖源文本或策略的变动。策略过期的词条会被列出,但仅在传入 `--refresh-policy` 时才会触发重新翻译。系统绝不会自动覆盖手动编辑的内容。初次为已审校的译文引入清单文件,或明确将人工修改确认采纳为新基线时,请使用 `--adopt-existing`。
+翻译状态会独立报告缺失(missing)、源内容过期(source-stale)、策略过期(policy-stale)、最新(current)和手动编辑(manually edited)状态,因此手动编辑不会掩盖源内容或策略变更。策略过期的项会被报告,但仅在传入 `--refresh-policy` 时才会重新翻译。系统绝不会自动覆盖手动编辑的值。在初次将清单引入已审校的译文,或明确采纳已审校的手动编辑作为新基线时,请使用 `--adopt-existing`。
### `validate`
-将所有语言环境文件与源文件包进行比对。默认校验会计算必需目标翻译键的覆盖率,将多余翻译键列为警告,并在翻译键缺失、插值变量不匹配或 ICU MessageFormat 结构无效时失败。
+将所有语言环境文件与源资源包进行比对。默认校验会检查结构覆盖率(所需目标键存在的百分比),将多余的键报告为警告,并在出现键缺失、插值不匹配或 ICU MessageFormat 结构无效时失败退出。
```bash
-internationalizer validate # 人类可读文本输出
-internationalizer validate --json # 机器可读 JSON 输出
-internationalizer validate -q # 静默模式,仅返回退出码
-internationalizer validate --strict # 强制执行翻译质量规则
-internationalizer validate --require-state # 要求清单中的溯源状态为最新
+internationalizer validate # human-readable output
+internationalizer validate --json # machine-readable JSON
+internationalizer validate -q # exit code only
+internationalizer validate --strict # enforce translation quality rules
+internationalizer validate --require-state # require current manifest provenance
```
-`--strict` 还会报告实际翻译覆盖率。语言内容若与源文完全相同,会被视为未翻译;只有术语表为完整值明确配置了源文与译文相同的精确条目时才会豁免。校验会遵守 `ignore_case`,但较长文本中仅包含某个术语表词条并不能获得豁免。严格模式会在存在多余翻译键、与源文相同的值、插值/HTML/代码/Markdown 链接结构变更、术语表违规或缺少已配置复数形式时失败。
+`--strict` 还会报告已翻译覆盖率。如果语言文本与源文本相同,则被视为未翻译,除非术语表中明确包含一条针对该完整文本、且源文与目标文本完全一致的精确词条;校验支持 `ignore_case`,但较长文本中仅包含某术语词条并不能获得豁免。严格模式会在存在多余键、与源文本相同的值、插值/HTML/代码/Markdown 链接结构发生变更、术语表违规以及缺少已配置的复数形式时失败。
-`--require-state` 会将每个目标值与 `.internationalizer.lock` 比对。翻译键未被跟踪,或清单记录的源文、翻译策略、目标哈希已过期时,校验都会失败。该选项可与 `--strict` 同时使用。
+`--require-state` 会将每个目标项与 `.internationalizer.lock` 进行比对验证。当键未被跟踪,或者记录的源内容、翻译策略或目标哈希已过期时,校验将失败。该参数可与 `--strict` 结合使用。
-人工可读报告与 JSON 报告使用以下稳定的问题代码:
+人类可读报告与 JSON 报告均使用稳定的发现项代码:
| 代码 | 含义 |
| --- | --- |
-| `missing_key` / `extra_key` | 源文件与目标文件的翻译键集合不一致 |
-| `blank_translation` | 非空源文对应的严格模式译文为空 |
-| `source_identical` | 严格模式下语言内容仍与源文相同 |
-| `protected_structure_mismatch` | 插值、HTML、代码或链接结构发生变化 |
-| `glossary_violation` | 未找到获准使用的目标术语或变体 |
-| `plural_form_missing` | 缺少为该语言配置的复数形式 |
-| `icu_message_syntax` | 源文或译文的 ICU 消息格式有误 |
-| `icu_argument_mismatch` | ICU 参数名、参数类型或格式化样式不一致 |
-| `icu_selector_mismatch` | 选择器不一致,或复数类别不适用于目标语言 |
-| `untracked` | 清单中没有对应的目标记录 |
-| `source_stale` | 清单记录生成后源文发生变化 |
-| `policy_stale` | 生成提示词或模型设置发生变化 |
+| `missing_key` / `extra_key` | 源键集与目标键集不一致 |
+| `blank_translation` | 非空源内容对应的严格模式目标内容为空 |
+| `source_identical` | 严格模式下语言文本仍未翻译 |
+| `protected_structure_mismatch` | 插值、HTML、代码或链接结构发生变更 |
+| `glossary_violation` | 未找到获准的目标术语或变体 |
+| `plural_form_missing` | 缺少目标语言环境已配置的复数形式 |
+| `icu_message_syntax` | 源或目标 ICU 消息格式有误 |
+| `icu_argument_mismatch` | ICU 参数名、类型或格式化器样式不一致 |
+| `icu_selector_mismatch` | 选择器不一致,或复数类别不适用于目标语言环境 |
+| `untracked` | 清单中不存在该目标的记录 |
+| `source_stale` | 记录翻译后源内容发生变化 |
+| `policy_stale` | 生成的提示词或模型设置发生变化 |
| `target_modified` | 目标内容与清单记录不一致 |
### `detect`
-自动检测项目使用的 i18n 框架并生成推荐配置。
+自动检测项目所使用的 i18n 框架并给出配置建议。
```bash
internationalizer detect
```
-现已支持:react-i18next、next-intl、vue-i18n、纯 JSON 及 Markdown 文档。
+支持:react-i18next、next-intl、vue-i18n、普通 JSON、Markdown 文档。
### `glossary`
-管理在翻译期间强制遵循的单语言术语表。
+管理在翻译过程中强制执行的各语言专属术语表。
```bash
internationalizer glossary list --locale fr
@@ -187,27 +187,27 @@ internationalizer glossary remove --locale fr --source "Dashboard"
### `tm`
-管理翻译记忆库(基于 JSONL 存储的历史翻译缓存)。
+管理翻译记忆库(以 JSONL 缓存以往翻译的字符串)。
```bash
-internationalizer tm stats # 查看记录统计
-internationalizer tm export # 导出为 JSON 格式
-internationalizer tm clear --force # 清空全部记录
+internationalizer tm stats # show record counts
+internationalizer tm export # dump as JSON
+internationalizer tm clear --force # delete all records
```
-
+
## 配置参考
```yaml
# .internationalizer.yml
-# 源语言(默认:en)
+# Source language (default: en)
source_locale: en
-# 目标翻译语言列表(必填)
+# Languages to translate into (required)
target_locales: [fr, de, es, ja, yue, zh-CN, zh-TW, ar]
-# 一个或多个源到目标的映射绑定(必填)。
-# 占位符 {locale} 会被自动替换为各个已配置的目标语言标识。
+# One or more source-to-target mappings (required).
+# {locale} is replaced with each configured target locale.
bundles:
- id: app
source: locales/en.json
@@ -218,35 +218,35 @@ bundles:
target: docs/i18n/{locale}.md
format: markdown
-# 向后兼容配置:source_path 仍可将目标映射至同级文件,
-# 例如 locales/fr.json。新项目建议优先使用 bundles。
+# Backward compatibility: source_path still maps targets to sibling files
+# such as locales/fr.json. Prefer bundles for new projects.
# source_path: locales/en.json
-# LLM 服务商配置
+# LLM provider settings
llm:
- # 服务商选项:"anthropic"、"openai"、"gemini" 或 "openrouter"(默认:gemini)
+ # Provider: "anthropic", "openai", "gemini", or "openrouter" (default: gemini)
provider: gemini
- # 各服务商的默认模型:
+ # Model name defaults by provider:
# anthropic: claude-opus-5
- # openai: gpt-5.6-luna(思考力度默认设为 max)
+ # openai: gpt-5.6-luna (reasoning effort defaults to max)
# gemini: gemini-3.8-flash
# openrouter: deepseek/deepseek-v4-pro-0813
model: gemini-3.8-flash
- # 存储 API 密钥的环境变量名称
+ # Environment variable containing the API key
api_key_env: GOOGLE_AI_STUDIO_API_KEY
- # OpenAI 兼容接口的 Base URL(可选)
+ # Base URL for OpenAI-compatible endpoints (optional)
# base_url: https://api.openai.com
- # OpenAI GPT-5 系列 Responses API 的思考力度(reasoning effort)
- #(OpenAI 服务商默认值:max)
+ # OpenAI GPT-5-series Responses API reasoning effort
+ # (default: max for the OpenAI provider)
reasoning_effort: max
- # 针对特定目标语言的独立 LLM 设置(可选)。覆盖配置若沿用全局服务商,
- # 会自动继承全局中未显式指定的参数;若切换为其他服务商,
- # 未指定项则回退至该服务商的默认设置。
+ # Optional LLM settings for individual target locales. An override using the
+ # global provider inherits unspecified global settings. A different provider
+ # uses that provider's defaults for unspecified settings.
locale_overrides:
yue:
provider: openrouter
@@ -261,61 +261,63 @@ llm:
model: deepseek/deepseek-v4-flash-0731
api_key_env: OPENROUTER_API_KEY
-# 每次 LLM 请求包含的键数量(默认:40)
+# Keys per LLM call (default: 40)
batch_size: 40
-# 并行 LLM 请求数(默认:4)
+# Parallel LLM calls (default: 4)
concurrency: 4
-# 各语言风格指南 Markdown 文件存放目录(默认:style-guides)
+# Directory containing per-locale style guide Markdown files (default: style-guides)
style_guides_dir: style-guides
-# 各语言术语表 JSON 文件存放目录(默认:glossary)
+# Directory containing per-locale glossary JSON files (default: glossary)
glossary_dir: glossary
-# 翻译记忆库文件路径(默认:.internationalizer/tm.jsonl)
+# Path to translation memory file (default: .internationalizer/tm.jsonl)
tm_path: .internationalizer/tm.jsonl
-# 记录源文、策略、目标及版本溯源状态的清单文件
-#(默认:.internationalizer.lock;请将此文件提交至版本控制)
+# Versioned source, policy, target, and provenance state
+# (default: .internationalizer.lock; commit this file)
manifest_path: .internationalizer.lock
-# 可选的翻译及严格校验规则
+# Optional translation and strict-validation rules
validation:
- plural_style: i18next-v4 # 生成并校验目标语言的复数形式
+ plural_style: i18next-v4 # generate and validate target-locale plural forms
```
-语言标识必须是格式正确的 BCP 47 标签,例如 `fr`、`pt-BR` 或 `sr-Latn-RS`。规范化后等价的目标语言会被判定为重复项;各语言专属的服务商覆写配置也会按规范化形式匹配。上述示例中,日语等没有单独覆写配置的语言均继承全局 Gemini 设置。
-
-ICU MessageFormat 内容会按结构解析。系统支持简单参数、`select`、`plural`、`selectordinal`、`number`、`date` 和 `time`,并支持嵌套消息、复数偏移量、精确数字选择器以及 `#`。校验会检查语法、参数类型与格式化样式、复数偏移量、select 分支标识,以及目标语言适用的 CLDR 复数类别。若服务商返回的内容破坏这些约束,系统会在写入语言文件或翻译记忆库前拒绝该结果。
+语言环境标识符必须是格式规范的 BCP 47 标签,如 `fr`、`pt-BR` 或 `sr-Latn-RS`。规范等价的目标语言环境会被作为重复项拒绝,特定语言环境的服务商覆盖项也会按规范等价拼写进行匹配。在上述示例中,未配置覆盖项的语言环境(包括日语)将继承全局 Gemini 配置。
-启用 `i18next-v4` 后,系统会在翻译期间将识别到的源复数词族扩展为目标语言所需的 CLDR 类别。仅目标语言需要的类别会使用源词族的 `_other` 值作为翻译模板。严格校验要求目标语言所需的类别齐全,但不会强制保留目标语言不使用的源语言专属类别。
+ICU MessageFormat 文本会按结构解析。支持简单参数、`select`、`plural`、`selectordinal`、`number`、`date` 以及 `time`,包括嵌套消息、复数偏移量、精确数字选择器和 `#`。校验会检查语法、参数类型与格式化器样式、复数偏移量、select 分支一致性,以及目标语言环境的 CLDR 复数类别。破坏这些不变量的服务商输出会在写入语言环境文件或翻译记忆库记录前被直接拒收。
+使用 `i18next-v4` 时,识别到的源复数族在翻译期间会扩展为目标语言环境对应的 CLDR 类别。仅目标语言环境需要的类别会使用源复数族的 `_other` 值作为其翻译模板。严格校验要求这些目标类别齐全;对于不使用这些类别的目标语言环境,仅源语言存在的类别则是可选的。
+
## 风格指南
-风格指南是以 Markdown 格式编写的文件,会在翻译时直接注入 LLM 提示词中。借此可精准把控译文的语调、正式程度、排版规范以及特定语言的行文习惯。
+风格指南是注入到 LLM 翻译提示词中的 Markdown 文件。它们控制语气、正式度、排版格式以及其他特定语言的规范。
```
style-guides/
- _conventions.md # 面向所有语言的通用规范
- fr.md # 法语专属规则
- ja.md # 日语专属规则
- ar.md # 阿拉伯语专属规则
+ _conventions.md # shared rules for all languages
+ fr.md # French-specific rules
+ ja.md # Japanese-specific rules
+ ar.md # Arabic-specific rules
```
### 通用规范(`_conventions.md`)
-用于定义适用于所有语言的全局规则,包括:插值语法、HTML 标签保护策略、不同文案类型的表述规范(如按钮、表单标签与错误提示)等。
+定义适用于所有语言的规则:插值语法、HTML 保留规则、字符串类型约定(按钮与标签及错误的表述规范)等。
-### 各语言指南(`{locale}.md`)
+### 各语言专属指南(`{locale}.md`)
-用于定义特定语言的细分规则,包括:正式程度(如法语 tu 与 vous)、特定标点规范(如法文引号 « »、西班牙语倒问号 ¿)、复数形式定义、日期/数字格式化及特定术语表。
+定义特定语言规则:正式度语体(tu 与 vous)、标点符号(书名号/引号、倒问号)、复数形式、日期/数字格式化,以及术语表。
-完整可运行范例参见 [`examples/react-app/style-guides/`](../../examples/react-app/style-guides/)。
+风格指南是持久的策略输入,而非生成的输出。Internationalizer 读取它们但绝不重写。其内容与术语表和提示词契约分开哈希,因此应用程序代码的更改不会导致翻译过期。修改指南会有意将该语言环境标记为策略待复查;更改内部提示词措辞则不会,除非提示词契约版本也同时变更。
+完整示例请参见 [`examples/react-app/style-guides/`](../../examples/react-app/style-guides/)。
+
## 术语表格式
-术语表以 JSON 数组形式保存在 `{glossary_dir}/{locale}.json` 中:
+术语表文件是保存在 `{glossary_dir}/{locale}.json` 中的 JSON 数组:
```json
[
@@ -330,78 +332,80 @@ style-guides/
]
```
-`variants` 用于列出其他获准使用的译法。`enforcement` 可设为 `error` 或 `warning`;省略时默认为 `error`。这些词条会作为术语对照表注入 LLM 提示词,确保整个应用中的术语保持一致。对于 `{"source":"API","target":"API"}` 这类源文与译文完全相同的精确条目,严格校验不会将完整值判定为未翻译;较长文本中仅包含 `API` 则不会获得豁免。
-
+`variants` 列出其他批准的目标形式。`enforcement` 可以是 `error`、`warning`,或者省略以采用默认的错误行为。术语会作为术语表注入到 LLM 提示词中,以确保全应用翻译的一致性。类似 `{"source":"API","target":"API"}` 这样源文与目标完全一致的精确词条,也会免除该完整相同值在严格模式下的未翻译发现项;但仅包含 `API` 的较长文本并不能获得豁免。
+
## 翻译记忆库
-翻译记忆库采用 JSONL 文件格式存储(每行对应一条独立 JSON 记录)。各记录包含以下字段:
+翻译记忆库存储为 JSONL 文件(每行一个 JSON 记录)。每条记录包含:
-- 文件包、翻译键、源文、译文及规范化后的目标语言
-- 源文哈希与翻译策略哈希
-- 生成译文的服务商与模型
+- 资源包、键、源值、翻译值及规范的目标语言环境
+- 源内容、风格指南、术语表、提示词契约以及合并策略哈希
+- 生成翻译的服务商与模型
- 时间戳
-后续翻译时,源文哈希与策略哈希均相同的文本会直接从缓存复用,无需调用 LLM。默认路径位于已被忽略的 `.internationalizer/` 目录中,因此它是本地缓存。如需由项目成员共享翻译记忆库,请将 `tm_path` 明确设为受版本控制的路径。可审查的 `.internationalizer.lock` 清单则单独纳入版本控制。
-
-## 支持的文件格式
+在随后的运行中,具有相同源内容和策略哈希的字符串将直接从缓存中获取,而无需调用 LLM。默认路径位于被忽略的 `.internationalizer/` 目录中,因此它作为本地缓存存在。如果项目需要显式共享翻译记忆库,请将 `tm_path` 设置为版本控制跟踪的路径。可供审查的 `.internationalizer.lock` 清单则单独进行版本控制。
+
+## 支持的格式
-| 格式 | 文件扩展名 | 处理模式 |
+| 格式 | 扩展名 | 模式 |
|--------|-----------|------|
-| JSON | `.json` | 键值对(支持深层嵌套,按点号表示法扁平化) |
-| YAML | `.yml`、`.yaml` | 键值对(完整保留注释与键序) |
-| Markdown | `.md`、`.mdx` | 整篇文档全文翻译 |
+| JSON | `.json` | 键值对(嵌套结构,按点号表示法扁平化) |
+| YAML | `.yml`, `.yaml` | 键值对(保留注释与顺序) |
+| Markdown | `.md`, `.mdx` | 序言与二级标题(H2)级别章节 |
-## 项目类型检测机制
+Markdown 目标文件在二级标题前包含不可见的 `internationalizer:unit` 注释。这些稳定标记使 Internationalizer 能够新增、移动或编辑单个源章节,而无需重新翻译不相关的章节。现有未标记的文档将在下一次成功更新时添加标记。
+
+## 项目类型检测
-执行 `internationalizer detect` 时,工具将通过检查以下特征定位您的 i18n 环境:
+`internationalizer detect` 通过检查以下内容来识别您的 i18n 配置:
-- `package.json` 中的 react-i18next、next-intl 或 vue-i18n 依赖
-- 符合通用规范的语言目录架构
+- `package.json` 中是否存在 react-i18next、next-intl 或 vue-i18n 依赖
+- 是否存在匹配通用区域设置模式的目录结构
- 文件扩展名与命名约定
-
-## 系统架构
+
+## 架构
```
-cmd/internationalizer/ CLI 入口点与命令定义
+cmd/internationalizer/ CLI entry point and command definitions
internal/
- config/ YAML 配置加载与默认值填充
- detect/ 项目类型自动检测
- formats/ 格式解析器(JSON、YAML、Markdown)
- glossary/ 单语言术语表管理
- llm/ LLM 服务商通用接口与后端实现
- anthropic.go Anthropic Claude 后端
- openai.go OpenAI 及兼容后端
- gemini.go 基于 Google AI Studio 的 Gemini 后端
- OpenRouter 复用 openai.go 并指定自定义 base_url
- locale/ BCP 47 语言标识与 CLDR 复数类别
- message/ ICU MessageFormat 解析器与结构比对
- policy/ 稳定的翻译策略哈希
- state/ 受版本控制的翻译清单
- styleguide/ 风格指南加载器
- tm/ JSONL 翻译记忆库实现
- translate/ 翻译流水线核心编排器
- validate/ 语言包校验与差异分析
+ config/ YAML config loading with defaults
+ detect/ Project type auto-detection
+ formats/ Format parsers (JSON, YAML, Markdown)
+ glossary/ Per-locale glossary management
+ llm/ LLM provider interface + implementations
+ anthropic.go Anthropic Claude backend
+ openai.go OpenAI / compatible backend
+ gemini.go Google Gemini via AI Studio backend
+ OpenRouter uses openai.go with custom base_url
+ locale/ BCP 47 identity and CLDR plural categories
+ message/ ICU MessageFormat parser and structural comparison
+ policy/ Stable translation-policy hashing
+ state/ Versioned translation manifest
+ styleguide/ Style guide loader
+ tm/ JSONL translation memory
+ translate/ Translation orchestrator
+ validate/ Locale validation and diffing
```
-
-## 与其他方案对比
+
+## 同类方案对比
| 功能特性 | Internationalizer | i18next | Crowdin | 通用 LLM |
|---------|------------------|---------|---------|-------------|
-| 基于 LLM 驱动翻译 | 是 | 否 | 部分 | 是 |
-| 支持各语言风格指南 | 是 | 否 | 否 | 否 |
-| 强制约束专业术语表 | 是 | 否 | 是 | 否 |
-| 内置翻译记忆库 | 是 | 否 | 是 | 否 |
-| 支持 CLI / 本地直接执行 | 是 | 不适用 | 否 | 需手动操作 |
-| 生成 Git 友好文件 | 是 | 是 | 部分 | 需手动操作 |
-| 摆脱商业 SaaS 依赖 | 是 | 是 | 否 | 视情况而定 |
-| 完全开源(AGPL-3.0) | 是 | 是 | 否 | 视情况而定 |
-
+| LLM 驱动翻译 | 是 | 否 | 部分 | 是 |
+| 各语言专属风格指南 | 是 | 否 | 否 | 否 |
+| 术语表强制执行 | 是 | 否 | 是 | 否 |
+| 翻译记忆库 | 是 | 否 | 是 | 否 |
+| CLI / 本地执行 | 是 | 不适用 | 否 | 手动 |
+| Git 友好文件 | 是 | 是 | 部分 | 手动 |
+| 无 SaaS 依赖 | 是 | 是 | 否 | 视情况而定 |
+| 开源(AGPL-3.0) | 是 | 是 | 否 | 视情况而定 |
+
## 许可证
-本项目遵循 [AGPL-3.0](../../LICENSE) 协议开源。
-
-第三方依赖声明请参阅 [THIRD_PARTY_NOTICES.md](../../THIRD_PARTY_NOTICES.md)。
+[AGPL-3.0](../../LICENSE)
-## 参与贡献
+有关依赖项声明,请参见 [THIRD_PARTY_NOTICES.md](../../THIRD_PARTY_NOTICES.md)。
+
+## 贡献指南
-本地开发环境搭建与贡献指引请参阅 [CONTRIBUTING.md](../../CONTRIBUTING.md)。所有代码提交均需包含 DCO 签署。
+有关开发环境搭建与贡献指南,请参见 [CONTRIBUTING.md](../../CONTRIBUTING.md)。所有贡献均需要 DCO 签署。
diff --git a/docs/i18n/zh-TW.md b/docs/i18n/zh-TW.md
index 074f23f..aaddc5b 100644
--- a/docs/i18n/zh-TW.md
+++ b/docs/i18n/zh-TW.md
@@ -17,38 +17,40 @@
---
+
## 為什麼選擇 Internationalizer?
-大多數的 i18n 工具不是執行階段函式庫(i18next、react-intl),就是翻譯鍵管理 SaaS 平台(Crowdin、Lokalise)。它們都無法妥善解決核心的翻譯問題:
+大多數 i18n 工具不是執行階段函式庫(i18next、react-intl),就是鍵值管理 SaaS 平台(Crowdin、Lokalise)。它們都無法妥善解決核心的翻譯問題:
-- **手動翻譯**在語言數量增加後難以擴展
-- **機器翻譯 API**(Google Translate、DeepL)會忽略您的專屬術語、語氣與 UI 慣例
-- **通用 LLM 翻譯**效果較佳,但若缺乏詞彙表與風格指南,產出的結果容易前後不一
+- **人工翻譯**在語言數量超過少數幾種後便無法有效擴展
+- **機器翻譯 API**(Google Translate、DeepL)會忽略您的術語、語氣與 UI 慣例
+- **通用 LLM 翻譯**效果較好,但若缺乏詞彙表與風格指南,產出的結果容易前後不一
-Internationalizer 截然不同。它是一個結合了 LLM 翻譯與下列功能的 **CLI 管線**:
+Internationalizer 截然不同。它是一個結合了 LLM 翻譯與下列特點的 **CLI 管線**:
-- **各語言專屬詞彙表** — 確保整個應用程式中的術語維持一致
-- **各語言專屬風格指南** — 精確控制語氣、正式程度、複數規則與文字排版
-- **翻譯記憶庫** — 自動跳過未變更的字串,節省 API 呼叫費用
-- **確定性驗證** — 在發布前找出翻譯鍵遺漏或多餘、受保護結構變更、詞彙表違規,以及複數或 ICU 錯誤
+- **各語言專屬詞彙表** — 在整個應用程式中強制維持一致的術語
+- **各語言專屬風格指南** — 控制語氣、正式程度、複數規則與文字排版
+- **翻譯記憶庫** — 跳過未變更的字串,節省 API 呼叫費用
+- **確定性驗證** — 在發布前找出缺少或多餘的鍵、受保護結構漂移、詞彙表問題,以及複數或 ICU 錯誤
+
## 安裝
-透過 npm 安裝:
+從 npm 安裝:
```bash
npm install -g internationalizer
```
-或者無需全域安裝,直接執行:
+或者無需全域安裝即可執行:
```bash
npx internationalizer --help
```
-npm 套件會藉由平台專屬的選用相依性,從 npm 安裝相對應的預先建置二進位檔。
+npm 套件會透過平台專屬的選用相依性,從 npm 安裝相對應的預先建置二進位檔。
-透過 Go 安裝:
+使用 Go 安裝:
```bash
go install github.com/Tom-R-Main/Internationalizer/cmd/internationalizer@latest
@@ -62,16 +64,18 @@ cd Internationalizer
go build -o internationalizer ./cmd/internationalizer
```
+
## npm 套件
-- Git 標籤與 npm 套件版本必須完全一致,例如 `v0.1.0` 與 `0.1.0`
-- 根目錄的 `internationalizer` 套件相依於各平台套件,例如 `internationalizer-darwin-arm64`
+- Git 標籤與 npm 套件版本必須相符,例如 `v0.1.0` 與 `0.1.0`
+- 根目錄的 `internationalizer` 套件相依於平台套件,例如 `internationalizer-darwin-arm64`
- 支援的 npm 目標平台:macOS arm64/x64、Linux arm64/x64、Windows x64
-- CI 發布流程需要名為 `NPM_TOKEN` 的 GitHub secret
+- CI 發布需要名為 `NPM_TOKEN` 的 GitHub secret
+
## 快速入門
-1. 在專案根目錄建立設定檔:
+1. 在您的專案根目錄建立設定檔:
```yaml
# .internationalizer.yml
@@ -95,7 +99,7 @@ llm:
export GOOGLE_AI_STUDIO_API_KEY=your-ai-studio-key
```
-3. 預覽將要翻譯的內容:
+3. 預覽將翻譯的內容:
```bash
internationalizer translate --dry-run
@@ -107,73 +111,74 @@ internationalizer translate --dry-run
internationalizer translate
```
-5. 驗證所有語系:
+5. 驗證所有語言地區:
```bash
internationalizer validate
```
+
## 指令
### `translate`
-找出遺漏或已過期的翻譯鍵並透過 LLM 進行翻譯。
+尋找缺少或過期的鍵,並透過 LLM 進行翻譯。
```bash
-internationalizer translate # 翻譯所有語系
-internationalizer translate -l fr # 僅翻譯法文
-internationalizer translate --dry-run # 預覽翻譯內容,不呼叫 API
-internationalizer translate --adopt-existing # 將現有翻譯設為基準,不呼叫 API
-internationalizer translate --refresh-policy # 重新整理因提示詞/風格/模型變更而過期的項目
-internationalizer translate --batch-size 20 # 縮小每批次翻譯量
-internationalizer translate --concurrency 2 # 降低平行呼叫數
+internationalizer translate # translate all locales
+internationalizer translate -l fr # translate French only
+internationalizer translate --dry-run # preview without API calls
+internationalizer translate --adopt-existing # baseline existing translations without API calls
+internationalizer translate --refresh-policy # refresh prompt/style/model-stale entries
+internationalizer translate --batch-size 20 # smaller batches
+internationalizer translate --concurrency 2 # fewer parallel calls
```
-翻譯狀態會獨立回報遺漏、來源過期、政策過期、最新與手動編輯等情況,因此手動編輯不會掩蓋來源或政策的變更。政策過期的內容會被列出回報,但僅在附加 `--refresh-policy` 旗標時才會重新翻譯。系統絕不會自動覆寫手動編輯的內容。初次將資訊清單(manifest)導入已審核的翻譯,或明確要將審核後的手動編輯採納為新基準時,請使用 `--adopt-existing`。
+翻譯狀態會獨立回報缺少、來源過期、政策過期、最新以及手動編輯等情況,因此手動編輯不會掩蓋來源或政策的變更。政策過期的值會被回報,但僅在附加 `--refresh-policy` 時才會重新翻譯。手動編輯的值絕不會被自動覆寫。當初次將資訊清單導入已審核的翻譯,或明確要將審核後的手動編輯採納為新基準時,請使用 `--adopt-existing`。
### `validate`
-將所有語系檔案與來源套件進行比對。預設驗證會計算必要目標翻譯鍵的涵蓋率,將多餘的翻譯鍵列為警告,並在翻譯鍵遺漏、插值不符或 ICU MessageFormat 結構無效時失敗。
+依據來源套件檢查所有語言地區檔案。預設驗證會檢查結構涵蓋率(現存必要目標鍵的百分比),將多餘的鍵回報為警告,並在缺少鍵、插值不符或 ICU MessageFormat 結構無效時判定為失敗。
```bash
-internationalizer validate # 人類可讀的輸出
-internationalizer validate --json # 機器可讀的 JSON
-internationalizer validate -q # 僅傳回結束代碼
-internationalizer validate --strict # 強制執行翻譯品質規則
-internationalizer validate --require-state # 要求資訊清單中的來源追溯狀態為最新
+internationalizer validate # human-readable output
+internationalizer validate --json # machine-readable JSON
+internationalizer validate -q # exit code only
+internationalizer validate --strict # enforce translation quality rules
+internationalizer validate --require-state # require current manifest provenance
```
-`--strict` 也會回報實際翻譯涵蓋率。語言內容若與來源完全相同,便會視為未翻譯;只有詞彙表針對完整值明確設定來源與譯文相同的精確項目時才會豁免。驗證會遵守 `ignore_case`,但較長內容中只包含某個詞彙表項目並不會獲得豁免。嚴格模式會在出現多餘翻譯鍵、與來源相同的值、插值/HTML/程式碼/Markdown 連結結構變更、詞彙表違規或缺少已設定的複數形式時失敗。
+`--strict` 也會回報翻譯涵蓋率。語言內容若與其來源完全相同,除非詞彙表明確包含該完整值且來源與目標相同的精確項目,否則將視為未翻譯;驗證會遵循 `ignore_case`,但較長值中僅嵌入詞彙表術語並不會獲得豁免。嚴格模式會在出現多餘的鍵、與來源相同的值、插值/HTML/程式碼/Markdown 連結結構變更、詞彙表違規以及已設定的複數形式缺失時判定為失敗。
-`--require-state` 會將每個目標值與 `.internationalizer.lock` 比對。翻譯鍵未受追蹤,或資訊清單記錄的來源、翻譯政策、目標雜湊已過期時,驗證都會失敗。此選項可與 `--strict` 同時使用。
+`--require-state` 會對照 `.internationalizer.lock` 驗證每個目標。當鍵未受追蹤,或其記錄的來源、翻譯政策或目標雜湊已過期時,驗證將判定為失敗。此旗標可與 `--strict` 搭配使用。
-人類可讀報告與 JSON 報告使用下列穩定的問題代碼:
+人類可讀與 JSON 報告均使用穩定的檢查結果代碼:
-| 代碼 | 含義 |
+| 代碼 | 意義 |
| --- | --- |
-| `missing_key` / `extra_key` | 來源與目標的翻譯鍵集合不一致 |
-| `blank_translation` | 非空來源所對應的嚴格模式譯文為空 |
-| `source_identical` | 嚴格模式下語言內容仍與來源相同 |
-| `protected_structure_mismatch` | 插值、HTML、程式碼或連結結構發生變更 |
-| `glossary_violation` | 找不到獲准使用的目標術語或變體 |
-| `plural_form_missing` | 缺少為該語言設定的複數形式 |
-| `icu_message_syntax` | 來源或譯文的 ICU 訊息格式有誤 |
-| `icu_argument_mismatch` | ICU 引數名稱、類型或格式樣式不一致 |
-| `icu_selector_mismatch` | 選擇器不一致,或複數類別不適用於目標語言 |
-| `untracked` | 資訊清單中沒有對應的目標記錄 |
-| `source_stale` | 資訊清單記錄建立後來源內容發生變更 |
-| `policy_stale` | 產生提示詞或模型設定發生變更 |
-| `target_modified` | 目標內容與資訊清單記錄不一致 |
+| `missing_key` / `extra_key` | 來源與目標鍵集合不符 |
+| `blank_translation` | 非空的來源在嚴格模式下對應了空的目標值 |
+| `source_identical` | 嚴格模式下語言值仍未翻譯 |
+| `protected_structure_mismatch` | 插值、HTML、程式碼或連結結構已變更 |
+| `glossary_violation` | 找不到已核准的目標術語或變體 |
+| `plural_form_missing` | 缺少已設定的語言地區複數形式 |
+| `icu_message_syntax` | 來源或目標 ICU 訊息語法格式錯誤 |
+| `icu_argument_mismatch` | ICU 引數名稱、類型或格式器樣式不符 |
+| `icu_selector_mismatch` | 選擇器不符,或複數類別對目標語言地區無效 |
+| `untracked` | 資訊清單中不存在該目標的記錄 |
+| `source_stale` | 記錄翻譯後來源內容已變更 |
+| `policy_stale` | 產生的提示詞或模型設定已變更 |
+| `target_modified` | 目標內容與資訊清單記錄不符 |
### `detect`
-自動偵測 i18n 框架並提供建議設定。
+自動偵測 i18n 框架並建議設定。
```bash
internationalizer detect
```
-支援:react-i18next、next-intl、vue-i18n、純 JSON、Markdown 文件。
+支援:react-i18next、next-intl、vue-i18n、原生 JSON、Markdown 文件。
### `glossary`
@@ -187,27 +192,28 @@ internationalizer glossary remove --locale fr --source "Dashboard"
### `tm`
-管理翻譯記憶庫(儲存先前翻譯字串的 JSONL 快取)。
+管理翻譯記憶庫(先前翻譯字串的 JSONL 快取)。
```bash
-internationalizer tm stats # 顯示記錄總數
-internationalizer tm export # 匯出為 JSON
-internationalizer tm clear --force # 刪除所有記錄
+internationalizer tm stats # show record counts
+internationalizer tm export # dump as JSON
+internationalizer tm clear --force # delete all records
```
+
## 設定參考
```yaml
# .internationalizer.yml
-# 來源語言(預設:en)
+# Source language (default: en)
source_locale: en
-# 要翻譯成的目標語言(必填)
+# Languages to translate into (required)
target_locales: [fr, de, es, ja, yue, zh-CN, zh-TW, ar]
-# 一或多個來源到目標的對應設定(必填)。
-# {locale} 會替換為每個設定的目標語言代碼。
+# One or more source-to-target mappings (required).
+# {locale} is replaced with each configured target locale.
bundles:
- id: app
source: locales/en.json
@@ -218,35 +224,35 @@ bundles:
target: docs/i18n/{locale}.md
format: markdown
-# 向後相容設定:source_path 仍可將目標對應至同層檔案,
-# 例如 locales/fr.json。新專案建議優先使用 bundles。
+# Backward compatibility: source_path still maps targets to sibling files
+# such as locales/fr.json. Prefer bundles for new projects.
# source_path: locales/en.json
-# LLM 供應商設定
+# LLM provider settings
llm:
- # 供應商:"anthropic"、"openai"、"gemini" 或 "openrouter"(預設:gemini)
+ # Provider: "anthropic", "openai", "gemini", or "openrouter" (default: gemini)
provider: gemini
- # 各供應商的預設模型名稱:
+ # Model name defaults by provider:
# anthropic: claude-opus-5
- # openai: gpt-5.6-luna(推理程度預設為 max)
+ # openai: gpt-5.6-luna (reasoning effort defaults to max)
# gemini: gemini-3.8-flash
# openrouter: deepseek/deepseek-v4-pro-0813
model: gemini-3.8-flash
- # 存放 API 金鑰的環境變數名稱
+ # Environment variable containing the API key
api_key_env: GOOGLE_AI_STUDIO_API_KEY
- # 相容於 OpenAI 端點的基礎 URL(選填)
+ # Base URL for OpenAI-compatible endpoints (optional)
# base_url: https://api.openai.com
- # OpenAI GPT-5 系列 Responses API 的推理程度
- #(OpenAI 供應商預設值:max)
+ # OpenAI GPT-5-series Responses API reasoning effort
+ # (default: max for the OpenAI provider)
reasoning_effort: max
- # 個別目標語言的選用 LLM 設定。若覆寫時沿用全域供應商,
- # 未指定的項目將繼承全域設定;若改用其他供應商,
- # 未指定的項目則採用該供應商的預設值。
+ # Optional LLM settings for individual target locales. An override using the
+ # global provider inherits unspecified global settings. A different provider
+ # uses that provider's defaults for unspecified settings.
locale_overrides:
yue:
provider: openrouter
@@ -261,58 +267,62 @@ llm:
model: deepseek/deepseek-v4-flash-0731
api_key_env: OPENROUTER_API_KEY
-# 單次 LLM 呼叫處理的鍵數量(預設:40)
+# Keys per LLM call (default: 40)
batch_size: 40
-# 平行 LLM 呼叫數量(預設:4)
+# Parallel LLM calls (default: 4)
concurrency: 4
-# 存放各語言風格指南 Markdown 檔案的目錄(預設:style-guides)
+# Directory containing per-locale style guide Markdown files (default: style-guides)
style_guides_dir: style-guides
-# 存放各語言詞彙表 JSON 檔案的目錄(預設:glossary)
+# Directory containing per-locale glossary JSON files (default: glossary)
glossary_dir: glossary
-# 翻譯記憶庫檔案路徑(預設:.internationalizer/tm.jsonl)
+# Path to translation memory file (default: .internationalizer/tm.jsonl)
tm_path: .internationalizer/tm.jsonl
-# 來源、政策、目標及來源追溯資訊的版本化狀態
-#(預設:.internationalizer.lock;請將此檔案提交至版本控制)
+# Versioned source, policy, target, and provenance state
+# (default: .internationalizer.lock; commit this file)
manifest_path: .internationalizer.lock
-# 選用的翻譯及嚴格驗證規則
+# Optional translation and strict-validation rules
validation:
- plural_style: i18next-v4 # 產生並驗證目標語言的複數形式
+ plural_style: i18next-v4 # generate and validate target-locale plural forms
```
-語言識別碼必須是格式正確的 BCP 47 標籤,例如 `fr`、`pt-BR` 或 `sr-Latn-RS`。正規化後等價的目標語言會被判定為重複項目;各語言專屬的供應商覆寫設定也會依正規化形式比對。上述範例中,日文等沒有個別覆寫設定的語言都會繼承全域 Gemini 設定。
+語言地區識別碼必須是格式正確的 BCP 47 標籤,例如 `fr`、`pt-BR` 或 `sr-Latn-RS`。規範等價的目標語言地區會視為重複項目而遭拒絕,且各語言地區專屬的供應商覆寫會比對規範等價拼寫。在上述範例中,未設定覆寫的語言地區(包括日文)會繼承全域 Gemini 設定。
-ICU MessageFormat 內容會依結構解析。系統支援簡單引數、`select`、`plural`、`selectordinal`、`number`、`date` 與 `time`,以及巢狀訊息、複數偏移量、精確數字選擇器和 `#`。驗證會檢查語法、引數類型與格式樣式、複數偏移量、select 分支識別,以及目標語言適用的 CLDR 複數類別。若供應商回傳的內容破壞這些條件,系統會在寫入語系檔案或翻譯記憶庫前拒絕該結果。
+ICU MessageFormat 值會進行結構化解析。系統支援簡易引數、`select`、`plural`、`selectordinal`、`number`、`date` 以及 `time`,包括巢狀訊息、複數偏移量、精確數字選擇器與 `#`。驗證會檢查語法、引數類型與格式器樣式、複數偏移量、select 分支一致性,以及目標語言地區 CLDR 複數類別。破壞這些不變條件的供應商輸出會在寫入語言地區檔案或翻譯記憶庫記錄之前遭到拒絕。
-啟用 `i18next-v4` 後,系統會在翻譯期間將辨識出的來源複數詞組擴充為目標語言需要的 CLDR 類別。只有目標語言需要的類別會使用來源詞組的 `_other` 值作為翻譯範本。嚴格驗證要求目標語言所需的類別齊全,但不強制保留目標語言不使用的來源語言專屬類別。
+在 `i18next-v4` 下,已識別的來源複數群組會在翻譯期間擴充為目標語言地區的 CLDR 類別。僅目標端存在的類別會使用來源群組的 `_other` 值作為其翻譯範本。嚴格驗證要求具備這些目標類別;若目標語言地區不使用某些僅來源端具備的類別,則這些類別為選填。
+
## 風格指南
-風格指南是會直接注入到 LLM 翻譯提示詞中的 Markdown 檔案。可用來控制語氣、正式程度、文字排版與其他語言專屬的慣例。
+風格指南是會被注入到 LLM 翻譯提示詞中的 Markdown 檔案。它們控制語氣、正式程度、文字排版與其他特定語言慣例。
```
style-guides/
- _conventions.md # 適用於所有語言的共用規則
- fr.md # 法文專屬規則
- ja.md # 日文專屬規則
- ar.md # 阿拉伯文專屬規則
+ _conventions.md # shared rules for all languages
+ fr.md # French-specific rules
+ ja.md # Japanese-specific rules
+ ar.md # Arabic-specific rules
```
### 共用慣例(`_conventions.md`)
-定義通用於所有語言的規範:變數插值語法、HTML 標籤保留、字串類型慣例(按鈕、標籤與錯誤訊息)等。
+定義適用於所有語言的規則:插值語法、HTML 保留、字串類型慣例(按鈕與標籤與錯誤訊息等)。
-### 各語言專屬指南(`{locale}.md`)
+### 各語言指南(`{locale}.md`)
-定義特定語言的細部規則:正式程度(例如 tu 與 vous)、標點符號規範(法文引號、倒問號)、複數形式、日期/數字格式,以及術語對照清單。
+定義特定語言規則:正式程度語域(tu 與 vous)、標點符號(法文引號、倒問號)、複數形式、日期/數字格式,以及術語詞彙表。
-實際運作範例請參閱 [`examples/react-app/style-guides/`](../../examples/react-app/style-guides/)。
+風格指南屬於長期有效的政策輸入,而非產生的輸出。Internationalizer 會讀取它們但絕不重寫。其內容雜湊與詞彙表及提示詞協定分開計算,因此應用程式程式碼的變更不會使翻譯過期。編輯指南會特意將該語言地區標記為需進行政策審查;變更內部提示詞用詞則不會,除非提示詞協定版本同時變更。
+實作範例請參閱 [`examples/react-app/style-guides/`](../../examples/react-app/style-guides/)。
+
+
## 詞彙表格式
詞彙表檔案是儲存在 `{glossary_dir}/{locale}.json` 中的 JSON 陣列:
@@ -330,78 +340,87 @@ style-guides/
]
```
-`variants` 用來列出其他獲准使用的譯法。`enforcement` 可設為 `error` 或 `warning`;省略時預設為 `error`。這些項目會以術語對照表的形式注入 LLM 提示詞,確保整個應用程式中的術語維持一致。對於 `{"source":"API","target":"API"}` 這類來源與譯文完全相同的精確項目,嚴格驗證不會將完整值判定為未翻譯;較長內容中只包含 `API` 則不會獲得豁免。
+`variants` 列出其他已核准的目標形式。`enforcement` 可為 `error`、`warning`,或省略以採用預設的 error 行為。術語會以術語表形式注入至 LLM 提示詞中,確保整個應用程式的翻譯一致。完全相同的精確項目(如 `{"source":"API","target":"API"}`)亦會讓該完整同源值免於嚴格模式的未翻譯值檢查結果;但若僅是較長的值中包含 `API`,則不符合豁免條件。
+
## 翻譯記憶庫
-翻譯記憶庫儲存為 JSONL 格式(每行一筆 JSON 記錄)。每筆記錄包含:
+翻譯記憶庫儲存為 JSONL 檔案(每行一個 JSON 記錄)。每個記錄包含:
-- 套件、翻譯鍵、來源值、譯文及正規化後的目標語言
-- 來源雜湊與翻譯政策雜湊
-- 產生譯文的供應商與模型
+- 套件、鍵、來源值、翻譯後的值,以及標準目標語言地區
+- 來源、風格指南、詞彙表、提示詞協定與合併政策雜湊
+- 產生翻譯的供應商與模型
- 時間戳記
-後續執行時,來源雜湊與政策雜湊都相同的內容會直接從快取重複使用,不必呼叫 LLM。預設路徑位於已被忽略的 `.internationalizer/` 目錄中,因此它是本機快取。如需讓專案成員共用翻譯記憶庫,請將 `tm_path` 明確設為受版本控制的路徑。可供審查的 `.internationalizer.lock` 資訊清單則另行納入版本控制。
+在後續執行時,具有相同來源與政策雜湊的字串會直接從快取提供,無需呼叫 LLM。預設路徑位於被忽略的 `.internationalizer/` 目錄下,因此它保持為本機快取。如果您的專案有意共用翻譯記憶庫,請將 `tm_path` 設為受版本控制的位置。可供審查的 `.internationalizer.lock` 資訊清單則分開進行版本控制。
+
## 支援的格式
| 格式 | 副檔名 | 模式 |
|--------|-----------|------|
-| JSON | `.json` | 鍵值對(支援巢狀、以點號表示法扁平化) |
-| YAML | `.yml`、`.yaml` | 鍵值對(保留註解與欄位順序) |
-| Markdown | `.md`、`.mdx` | 全文件翻譯 |
+| JSON | `.json` | 鍵值對(巢狀、點記法扁平化) |
+| YAML | `.yml`, `.yaml` | 鍵值對(保留註解與排序) |
+| Markdown | `.md`, `.mdx` | 前言與 H2 層級章節 |
+
+Markdown 目標在 H2 章節前包含隱形的 `internationalizer:unit` 註解。這些穩定的標記讓 Internationalizer 能夠新增、移動或編輯單一來源章節,而無需重新翻譯不相關的章節。現有未標記的文件會在下次成功更新時獲得標記。
+
## 專案類型偵測
-執行 `internationalizer detect` 時,會透過檢查下列項目來識別您的 i18n 架構:
+`internationalizer detect` 透過檢查下列項目來識別您的 i18n 設定:
-- `package.json` 中對 react-i18next、next-intl 或 vue-i18n 的相依性
-- 符合常見語系結構的目錄配置
+- `package.json` 中 react-i18next、next-intl 或 vue-i18n 的相依性
+- 符合常見語言地區模式的目錄結構
- 檔案副檔名與命名慣例
+
## 架構
```
-cmd/internationalizer/ CLI 進入點與指令定義
+cmd/internationalizer/ CLI entry point and command definitions
internal/
- config/ YAML 設定載入與預設值處理
- detect/ 專案類型自動偵測
- formats/ 格式解析器(JSON、YAML、Markdown)
- glossary/ 各語言專屬詞彙表管理
- llm/ LLM 供應商介面與實作
- anthropic.go Anthropic Claude 後端
- openai.go OpenAI/相容端點後端
- gemini.go Google Gemini(透過 AI Studio 後端)
- OpenRouter 採用自訂 base_url 的 openai.go
- locale/ BCP 47 語言識別與 CLDR 複數類別
- message/ ICU MessageFormat 解析器與結構比對
- policy/ 穩定的翻譯政策雜湊
- state/ 受版本控制的翻譯資訊清單
- styleguide/ 風格指南載入器
- tm/ JSONL 翻譯記憶庫
- translate/ 翻譯流程協調器
- validate/ 語系驗證與差異比對
+ config/ YAML config loading with defaults
+ detect/ Project type auto-detection
+ formats/ Format parsers (JSON, YAML, Markdown)
+ glossary/ Per-locale glossary management
+ llm/ LLM provider interface + implementations
+ anthropic.go Anthropic Claude backend
+ openai.go OpenAI / compatible backend
+ gemini.go Google Gemini via AI Studio backend
+ OpenRouter uses openai.go with custom base_url
+ locale/ BCP 47 identity and CLDR plural categories
+ message/ ICU MessageFormat parser and structural comparison
+ policy/ Stable translation-policy hashing
+ state/ Versioned translation manifest
+ styleguide/ Style guide loader
+ tm/ JSONL translation memory
+ translate/ Translation orchestrator
+ validate/ Locale validation and diffing
```
+
## 與替代方案的比較
| 功能 | Internationalizer | i18next | Crowdin | 通用 LLM |
|---------|------------------|---------|---------|-------------|
| LLM 驅動翻譯 | 是 | 否 | 部分支援 | 是 |
| 各語言專屬風格指南 | 是 | 否 | 否 | 否 |
-| 強制套用詞彙表 | 是 | 否 | 是 | 否 |
+| 詞彙表強制執行 | 是 | 否 | 是 | 否 |
| 翻譯記憶庫 | 是 | 否 | 是 | 否 |
-| CLI/本機執行 | 是 | 不適用 | 否 | 手動處理 |
-| 對 Git 友善的檔案 | 是 | 是 | 部分支援 | 手動處理 |
-| 無需依賴 SaaS | 是 | 是 | 否 | 視情況而定 |
+| CLI/本機執行 | 是 | 不適用 | 否 | 手動 |
+| Git 友善檔案 | 是 | 是 | 部分支援 | 手動 |
+| 無 SaaS 相依性 | 是 | 是 | 否 | 視情況而定 |
| 開放原始碼(AGPL-3.0) | 是 | 是 | 否 | 視情況而定 |
+
## 授權條款
[AGPL-3.0](../../LICENSE)
-第三方相依套件聲明請參閱 [THIRD_PARTY_NOTICES.md](../../THIRD_PARTY_NOTICES.md)。
+相依性聲明請參閱 [THIRD_PARTY_NOTICES.md](../../THIRD_PARTY_NOTICES.md)。
+
## 貢獻指南
-開發環境設定與規範請參閱 [CONTRIBUTING.md](../../CONTRIBUTING.md)。所有貢獻均需完成 DCO 簽署。
+開發設定與指導原則請參閱 [CONTRIBUTING.md](../../CONTRIBUTING.md)。所有貢獻均需具備 DCO 簽名。